mirror of
https://github.com/RGBCube/serenity
synced 2025-05-14 09:14:58 +00:00
LibGfx+icc: Read and display lut16Type and lut8Type ICC tag types
This commit is contained in:
parent
a0513a360a
commit
909c2a73c4
4 changed files with 253 additions and 0 deletions
|
@ -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:
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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()) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue