Ver Fonte

LibGfx+icc: Read and display lut16Type and lut8Type ICC tag types

Nico Weber há 2 anos atrás
pai
commit
909c2a73c4

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

@@ -574,6 +574,10 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offse
     switch (type) {
     case CurveTagData::Type:
         return CurveTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
+    case Lut16TagData::Type:
+        return Lut16TagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element);
+    case Lut8TagData::Type:
+        return Lut8TagData::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 ParametricCurveTagData::Type:

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

@@ -28,6 +28,16 @@ struct XYZNumber {
     }
 };
 
+// Common bits of ICC v4, Table 40 — lut16Type encoding and Table 44 — lut8Type encoding
+struct LUTHeader {
+    u8 number_of_input_channels;
+    u8 number_of_output_channels;
+    u8 number_of_clut_grid_points;
+    u8 reserved_for_padding;
+    BigEndian<s15Fixed16Number> e_parameters[9];
+};
+static_assert(AssertSize<LUTHeader, 40>());
+
 ErrorOr<void> check_reserved(ReadonlyBytes tag_bytes)
 {
     if (tag_bytes.size() < 2 * sizeof(u32))
@@ -70,6 +80,107 @@ ErrorOr<NonnullRefPtr<CurveTagData>> CurveTagData::from_bytes(ReadonlyBytes byte
     return adopt_ref(*new CurveTagData(offset, size, move(values)));
 }
 
+ErrorOr<NonnullRefPtr<Lut16TagData>> Lut16TagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
+{
+    // ICC v4, 10.10 lut16Type
+    VERIFY(tag_type(bytes) == Type);
+    TRY(check_reserved(bytes));
+
+    if (bytes.size() < 2 * sizeof(u32) + sizeof(LUTHeader) + 2 + sizeof(u16))
+        return Error::from_string_literal("ICC::Profile: lut16Type has not enough data");
+
+    auto& header = *bit_cast<LUTHeader const*>(bytes.data() + 8);
+    if (header.reserved_for_padding != 0)
+        return Error::from_string_literal("ICC::Profile: lut16Type reserved_for_padding not 0");
+
+    u16 number_of_input_table_entries = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8 + sizeof(LUTHeader));
+    u16 number_of_output_table_entries = *bit_cast<BigEndian<u16> const*>(bytes.data() + 8 + sizeof(LUTHeader) + 2);
+    ReadonlyBytes table_bytes = bytes.slice(8 + sizeof(LUTHeader) + 4);
+
+    EMatrix e;
+    for (int i = 0; i < 9; ++i)
+        e.e[i] = S15Fixed16::create_raw(header.e_parameters[i]);
+
+    u32 input_tables_size = number_of_input_table_entries * header.number_of_input_channels;
+    u32 output_tables_size = number_of_output_table_entries * header.number_of_output_channels;
+    u32 clut_values_size = header.number_of_output_channels;
+    for (int i = 0; i < header.number_of_input_channels; ++i)
+        clut_values_size *= header.number_of_clut_grid_points;
+
+    if (table_bytes.size() < (input_tables_size + clut_values_size + output_tables_size) * sizeof(u16))
+        return Error::from_string_literal("ICC::Profile: lut16Type has not enough data for tables");
+
+    auto* raw_table_data = bit_cast<BigEndian<u16> const*>(table_bytes.data());
+
+    Vector<u16> input_tables;
+    input_tables.resize(input_tables_size);
+    for (u32 i = 0; i < input_tables_size; ++i)
+        input_tables[i] = raw_table_data[i];
+
+    Vector<u16> clut_values;
+    clut_values.resize(clut_values_size);
+    for (u32 i = 0; i < clut_values_size; ++i)
+        clut_values[i] = raw_table_data[input_tables_size + i];
+
+    Vector<u16> output_tables;
+    output_tables.resize(output_tables_size);
+    for (u32 i = 0; i < output_tables_size; ++i)
+        output_tables[i] = raw_table_data[input_tables_size + clut_values_size + i];
+
+    return adopt_ref(*new Lut16TagData(offset, size, e,
+        header.number_of_input_channels, header.number_of_output_channels, header.number_of_clut_grid_points,
+        number_of_input_table_entries, number_of_output_table_entries,
+        move(input_tables), move(clut_values), move(output_tables)));
+}
+
+ErrorOr<NonnullRefPtr<Lut8TagData>> Lut8TagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
+{
+    // ICC v4, 10.11 lut8Type
+    VERIFY(tag_type(bytes) == Type);
+    TRY(check_reserved(bytes));
+
+    if (bytes.size() < 8 + sizeof(LUTHeader))
+        return Error::from_string_literal("ICC::Profile: lut8Type has not enough data");
+
+    auto& header = *bit_cast<LUTHeader const*>(bytes.data() + 8);
+    if (header.reserved_for_padding != 0)
+        return Error::from_string_literal("ICC::Profile: lut16Type reserved_for_padding not 0");
+
+    u16 number_of_input_table_entries = 256;
+    u16 number_of_output_table_entries = 256;
+    ReadonlyBytes table_bytes = bytes.slice(8 + sizeof(LUTHeader));
+
+    EMatrix e;
+    for (int i = 0; i < 9; ++i)
+        e.e[i] = S15Fixed16::create_raw(header.e_parameters[i]);
+
+    u32 input_tables_size = number_of_input_table_entries * header.number_of_input_channels;
+    u32 output_tables_size = number_of_output_table_entries * header.number_of_output_channels;
+    u32 clut_values_size = header.number_of_output_channels;
+    for (int i = 0; i < header.number_of_input_channels; ++i)
+        clut_values_size *= header.number_of_clut_grid_points;
+
+    if (table_bytes.size() < input_tables_size + clut_values_size + output_tables_size)
+        return Error::from_string_literal("ICC::Profile: lut8Type has not enough data for tables");
+
+    Vector<u8> input_tables;
+    input_tables.resize(input_tables_size);
+    memcpy(input_tables.data(), table_bytes.data(), input_tables_size);
+
+    Vector<u8> clut_values;
+    clut_values.resize(clut_values_size);
+    memcpy(clut_values.data(), table_bytes.data() + input_tables_size, clut_values_size);
+
+    Vector<u8> output_tables;
+    output_tables.resize(output_tables_size);
+    memcpy(output_tables.data(), table_bytes.data() + input_tables_size + clut_values_size, output_tables_size);
+
+    return adopt_ref(*new Lut8TagData(offset, size, e,
+        header.number_of_input_channels, header.number_of_output_channels, header.number_of_clut_grid_points,
+        number_of_input_table_entries, number_of_output_table_entries,
+        move(input_tables), move(clut_values), move(output_tables)));
+}
+
 ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
 {
     // ICC v4, 10.15 multiLocalizedUnicodeType

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

@@ -86,6 +86,124 @@ private:
     Vector<u16> m_values;
 };
 
+struct EMatrix {
+    S15Fixed16 e[9];
+
+    S15Fixed16 const& operator[](int i) const
+    {
+        VERIFY(i >= 0 && i < 9);
+        return e[i];
+    }
+};
+
+// ICC v4, 10.10 lut16Type
+class Lut16TagData : public TagData {
+public:
+    static constexpr TagTypeSignature Type { 0x6D667432 }; // 'mft2'
+
+    static ErrorOr<NonnullRefPtr<Lut16TagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
+
+    Lut16TagData(u32 offset, u32 size, EMatrix e,
+        u8 number_of_input_channels, u8 number_of_output_channels, u8 number_of_clut_grid_points,
+        u16 number_of_input_table_entries, u16 number_of_output_table_entries,
+        Vector<u16> input_tables, Vector<u16> clut_values, Vector<u16> output_tables)
+        : TagData(offset, size, Type)
+        , m_e(e)
+        , m_number_of_input_channels(number_of_input_channels)
+        , m_number_of_output_channels(number_of_output_channels)
+        , m_number_of_clut_grid_points(number_of_clut_grid_points)
+        , m_number_of_input_table_entries(number_of_input_table_entries)
+        , m_number_of_output_table_entries(number_of_output_table_entries)
+        , m_input_tables(move(input_tables))
+        , m_clut_values(move(clut_values))
+        , m_output_tables(move(output_tables))
+    {
+        VERIFY(m_input_tables.size() == number_of_input_channels * number_of_input_table_entries);
+        VERIFY(m_output_tables.size() == number_of_output_channels * number_of_output_table_entries);
+    }
+
+    EMatrix const& e_matrix() const { return m_e; }
+
+    u8 number_of_input_channels() const { return m_number_of_input_channels; }
+    u8 number_of_output_channels() const { return m_number_of_output_channels; }
+    u8 number_of_clut_grid_points() const { return m_number_of_clut_grid_points; }
+
+    u16 number_of_input_table_entries() const { return m_number_of_input_table_entries; }
+    u16 number_of_output_table_entries() const { return m_number_of_output_table_entries; }
+
+    Vector<u16> const& input_tables() const { return m_input_tables; }
+    Vector<u16> const& clut_values() const { return m_clut_values; }
+    Vector<u16> const& output_tables() const { return m_output_tables; }
+
+private:
+    EMatrix m_e;
+
+    u8 m_number_of_input_channels;
+    u8 m_number_of_output_channels;
+    u8 m_number_of_clut_grid_points;
+
+    u16 m_number_of_input_table_entries;
+    u16 m_number_of_output_table_entries;
+
+    Vector<u16> m_input_tables;
+    Vector<u16> m_clut_values;
+    Vector<u16> m_output_tables;
+};
+
+// ICC v4, 10.11 lut8Type
+class Lut8TagData : public TagData {
+public:
+    static constexpr TagTypeSignature Type { 0x6D667431 }; // 'mft1'
+
+    static ErrorOr<NonnullRefPtr<Lut8TagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
+
+    Lut8TagData(u32 offset, u32 size, EMatrix e,
+        u8 number_of_input_channels, u8 number_of_output_channels, u8 number_of_clut_grid_points,
+        u16 number_of_input_table_entries, u16 number_of_output_table_entries,
+        Vector<u8> input_tables, Vector<u8> clut_values, Vector<u8> output_tables)
+        : TagData(offset, size, Type)
+        , m_e(e)
+        , m_number_of_input_channels(number_of_input_channels)
+        , m_number_of_output_channels(number_of_output_channels)
+        , m_number_of_clut_grid_points(number_of_clut_grid_points)
+        , m_number_of_input_table_entries(number_of_input_table_entries)
+        , m_number_of_output_table_entries(number_of_output_table_entries)
+        , m_input_tables(move(input_tables))
+        , m_clut_values(move(clut_values))
+        , m_output_tables(move(output_tables))
+    {
+        VERIFY(m_input_tables.size() == number_of_input_channels * number_of_input_table_entries);
+        VERIFY(m_output_tables.size() == number_of_output_channels * number_of_output_table_entries);
+    }
+
+    EMatrix const& e_matrix() const { return m_e; }
+
+    u8 number_of_input_channels() const { return m_number_of_input_channels; }
+    u8 number_of_output_channels() const { return m_number_of_output_channels; }
+    u8 number_of_clut_grid_points() const { return m_number_of_clut_grid_points; }
+
+    u16 number_of_input_table_entries() const { return m_number_of_input_table_entries; }
+    u16 number_of_output_table_entries() const { return m_number_of_output_table_entries; }
+
+    Vector<u8> const& input_tables() const { return m_input_tables; }
+    Vector<u8> const& clut_values() const { return m_clut_values; }
+    Vector<u8> const& output_tables() const { return m_output_tables; }
+
+private:
+    EMatrix m_e;
+
+    u8 m_number_of_input_channels;
+    u8 m_number_of_output_channels;
+    u8 m_number_of_clut_grid_points;
+
+    u16 m_number_of_input_table_entries;
+    u16 m_number_of_output_table_entries;
+
+    Vector<u8> m_input_tables;
+    Vector<u8> m_clut_values;
+    Vector<u8> m_output_tables;
+};
+
 // ICC v4, 10.15 multiLocalizedUnicodeType
 class MultiLocalizedUnicodeTagData : public TagData {
 public:

+ 20 - 0
Userland/Utilities/icc.cpp

@@ -135,6 +135,26 @@ ErrorOr<int> serenity_main(Main::Arguments arguments)
                 // FIXME: Maybe print the actual points if -v is passed?
                 outln("    curve with {} points", curve.values().size());
             }
+        } else if (tag_data->type() == Gfx::ICC::Lut16TagData::Type) {
+            auto& lut16 = static_cast<Gfx::ICC::Lut16TagData&>(*tag_data);
+            outln("    input table: {} channels x {} entries", lut16.number_of_input_channels(), lut16.number_of_input_table_entries());
+            outln("    output table: {} channels x {} entries", lut16.number_of_output_channels(), lut16.number_of_output_table_entries());
+            outln("    color lookup table: {} grid points, {} total entries", lut16.number_of_clut_grid_points(), lut16.clut_values().size());
+
+            auto const& e = lut16.e_matrix();
+            outln("    e = [ {}, {}, {},", e[0], e[1], e[2]);
+            outln("          {}, {}, {},", e[3], e[4], e[5]);
+            outln("          {}, {}, {} ]", e[6], e[7], e[8]);
+        } else if (tag_data->type() == Gfx::ICC::Lut8TagData::Type) {
+            auto& lut8 = static_cast<Gfx::ICC::Lut8TagData&>(*tag_data);
+            outln("    input table: {} channels x {} entries", lut8.number_of_input_channels(), lut8.number_of_input_table_entries());
+            outln("    output table: {} channels x {} entries", lut8.number_of_output_channels(), lut8.number_of_output_table_entries());
+            outln("    color lookup table: {} grid points, {} total entries", lut8.number_of_clut_grid_points(), lut8.clut_values().size());
+
+            auto const& e = lut8.e_matrix();
+            outln("    e = [ {}, {}, {},", e[0], e[1], e[2]);
+            outln("          {}, {}, {},", e[3], e[4], e[5]);
+            outln("          {}, {}, {} ]", e[6], e[7], e[8]);
         } 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()) {