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

This commit is contained in:
Nico Weber 2023-01-28 15:27:36 -05:00 committed by Linus Groh
parent a0513a360a
commit 909c2a73c4
Notes: sideshowbarker 2024-07-17 01:02:23 +09:00
4 changed files with 253 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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