diff options
author | Nico Weber <thakis@chromium.org> | 2023-01-28 15:27:36 -0500 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2023-01-28 21:40:45 +0000 |
commit | 909c2a73c4aa2359e3670b7e93d9a5051ba3881e (patch) | |
tree | 2d65f86ff2ff0cd6296d32fa623632a88979b1fd /Userland/Libraries | |
parent | a0513a360a6653a60a31b2827c8afea6fdd39fe7 (diff) | |
download | serenity-909c2a73c4aa2359e3670b7e93d9a5051ba3881e.zip |
LibGfx+icc: Read and display lut16Type and lut8Type ICC tag types
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/Profile.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/TagTypes.cpp | 111 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/TagTypes.h | 118 |
3 files changed, 233 insertions, 0 deletions
diff --git a/Userland/Libraries/LibGfx/ICC/Profile.cpp b/Userland/Libraries/LibGfx/ICC/Profile.cpp index 25efbebb40..f9b40aad03 100644 --- a/Userland/Libraries/LibGfx/ICC/Profile.cpp +++ b/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: diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp index 47a6933003..0c94bc4d8b 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp +++ b/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 diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.h b/Userland/Libraries/LibGfx/ICC/TagTypes.h index a6b579ec4d..a1afb083ab 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.h +++ b/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: |