summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2023-01-28 15:27:36 -0500
committerLinus Groh <mail@linusgroh.de>2023-01-28 21:40:45 +0000
commit909c2a73c4aa2359e3670b7e93d9a5051ba3881e (patch)
tree2d65f86ff2ff0cd6296d32fa623632a88979b1fd /Userland/Libraries
parenta0513a360a6653a60a31b2827c8afea6fdd39fe7 (diff)
downloadserenity-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.cpp4
-rw-r--r--Userland/Libraries/LibGfx/ICC/TagTypes.cpp111
-rw-r--r--Userland/Libraries/LibGfx/ICC/TagTypes.h118
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: