1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 06:27:45 +00:00

LibGfx: Implement serialization of LutAToBTagData and LutBToATagData

With this, we can write all tag types we can currently read :^)
This commit is contained in:
Nico Weber 2023-02-22 15:31:01 -05:00 committed by Andreas Kling
parent d50b4f3581
commit 7853be7869
2 changed files with 216 additions and 6 deletions

View file

@ -47,10 +47,13 @@ static ErrorOr<ByteBuffer> encode_cipc(CicpTagData const& tag_data)
return bytes; return bytes;
} }
static ErrorOr<ByteBuffer> encode_curve(CurveTagData const& tag_data) static u32 curve_encoded_size(CurveTagData const& tag_data)
{
return 3 * sizeof(u32) + tag_data.values().size() * sizeof(u16);
}
static void encode_curve_to(CurveTagData const& tag_data, Bytes bytes)
{ {
// ICC v4, 10.6 curveType
auto bytes = TRY(ByteBuffer::create_uninitialized(3 * sizeof(u32) + tag_data.values().size() * sizeof(u16)));
*bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(CurveTagData::Type); *bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(CurveTagData::Type);
*bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0; *bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
*bit_cast<BigEndian<u32>*>(bytes.data() + 8) = tag_data.values().size(); *bit_cast<BigEndian<u32>*>(bytes.data() + 8) = tag_data.values().size();
@ -58,7 +61,13 @@ static ErrorOr<ByteBuffer> encode_curve(CurveTagData const& tag_data)
auto* values = bit_cast<BigEndian<u16>*>(bytes.data() + 12); auto* values = bit_cast<BigEndian<u16>*>(bytes.data() + 12);
for (size_t i = 0; i < tag_data.values().size(); ++i) for (size_t i = 0; i < tag_data.values().size(); ++i)
values[i] = tag_data.values()[i]; values[i] = tag_data.values()[i];
}
static ErrorOr<ByteBuffer> encode_curve(CurveTagData const& tag_data)
{
// ICC v4, 10.6 curveType
auto bytes = TRY(ByteBuffer::create_uninitialized(curve_encoded_size(tag_data)));
encode_curve_to(tag_data, bytes.bytes());
return bytes; return bytes;
} }
@ -128,6 +137,190 @@ static ErrorOr<ByteBuffer> encode_lut_8(Lut8TagData const& tag_data)
return bytes; return bytes;
} }
static u32 curve_encoded_size(CurveTagData const&);
static void encode_curve_to(CurveTagData const&, Bytes);
static u32 parametric_curve_encoded_size(ParametricCurveTagData const&);
static void encode_parametric_curve_to(ParametricCurveTagData const&, Bytes);
static u32 byte_size_of_curve(LutCurveType const& curve)
{
VERIFY(curve->type() == Gfx::ICC::CurveTagData::Type || curve->type() == Gfx::ICC::ParametricCurveTagData::Type);
if (curve->type() == Gfx::ICC::CurveTagData::Type)
return curve_encoded_size(static_cast<CurveTagData const&>(*curve));
return parametric_curve_encoded_size(static_cast<ParametricCurveTagData const&>(*curve));
}
static u32 byte_size_of_curves(Vector<LutCurveType> const& curves)
{
u32 size = 0;
for (auto const& curve : curves)
size += align_up_to(byte_size_of_curve(curve), 4);
return size;
}
static void write_curve(Bytes bytes, LutCurveType const& curve)
{
VERIFY(curve->type() == Gfx::ICC::CurveTagData::Type || curve->type() == Gfx::ICC::ParametricCurveTagData::Type);
if (curve->type() == Gfx::ICC::CurveTagData::Type)
encode_curve_to(static_cast<CurveTagData const&>(*curve), bytes);
if (curve->type() == Gfx::ICC::ParametricCurveTagData::Type)
encode_parametric_curve_to(static_cast<ParametricCurveTagData const&>(*curve), bytes);
}
static void write_curves(Bytes bytes, Vector<LutCurveType> const& curves)
{
u32 offset = 0;
for (auto const& curve : curves) {
u32 size = byte_size_of_curve(curve);
write_curve(bytes.slice(offset, size), curve);
offset += align_up_to(size, 4);
}
}
static u32 byte_size_of_clut(CLUTData const& clut)
{
u32 data_size = clut.values.visit(
[](Vector<u8> const& v) { return v.size(); },
[](Vector<u16> const& v) { return 2 * v.size(); });
return align_up_to(sizeof(CLUTHeader) + data_size, 4);
}
static void write_clut(Bytes bytes, CLUTData const& clut)
{
auto& clut_header = *bit_cast<CLUTHeader*>(bytes.data());
memset(clut_header.number_of_grid_points_in_dimension, 0, sizeof(clut_header.number_of_grid_points_in_dimension));
VERIFY(clut.number_of_grid_points_in_dimension.size() <= sizeof(clut_header.number_of_grid_points_in_dimension));
for (size_t i = 0; i < clut.number_of_grid_points_in_dimension.size(); ++i)
clut_header.number_of_grid_points_in_dimension[i] = clut.number_of_grid_points_in_dimension[i];
clut_header.precision_of_data_elements = clut.values.visit(
[](Vector<u8> const&) { return 1; },
[](Vector<u16> const&) { return 2; });
memset(clut_header.reserved_for_padding, 0, sizeof(clut_header.reserved_for_padding));
clut.values.visit(
[&bytes](Vector<u8> const& v) {
memcpy(bytes.data() + sizeof(CLUTHeader), v.data(), v.size());
},
[&bytes](Vector<u16> const& v) {
auto* raw_clut = bit_cast<BigEndian<u16>*>(bytes.data() + sizeof(CLUTHeader));
for (size_t i = 0; i < v.size(); ++i)
raw_clut[i] = v[i];
});
}
static void write_matrix(Bytes bytes, EMatrix3x4 const& e_matrix)
{
auto* raw_e = bit_cast<BigEndian<s15Fixed16Number>*>(bytes.data());
for (int i = 0; i < 12; ++i)
raw_e[i] = e_matrix.e[i].raw();
}
static ErrorOr<ByteBuffer> encode_lut_a_to_b(LutAToBTagData const& tag_data)
{
// ICC v4, 10.12 lutAToBType
u32 a_curves_size = tag_data.a_curves().map(byte_size_of_curves).value_or(0);
u32 clut_size = tag_data.clut().map(byte_size_of_clut).value_or(0);
u32 m_curves_size = tag_data.m_curves().map(byte_size_of_curves).value_or(0);
u32 e_matrix_size = tag_data.e_matrix().has_value() ? 12 * sizeof(s15Fixed16Number) : 0;
u32 b_curves_size = byte_size_of_curves(tag_data.b_curves());
auto bytes = TRY(ByteBuffer::create_zeroed(2 * sizeof(u32) + sizeof(AdvancedLUTHeader) + a_curves_size + clut_size + m_curves_size + e_matrix_size + b_curves_size));
*bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(LutAToBTagData::Type);
*bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
auto& header = *bit_cast<AdvancedLUTHeader*>(bytes.data() + 8);
header.number_of_input_channels = tag_data.number_of_input_channels();
header.number_of_output_channels = tag_data.number_of_output_channels();
header.reserved_for_padding = 0;
header.offset_to_b_curves = 0;
header.offset_to_matrix = 0;
header.offset_to_m_curves = 0;
header.offset_to_clut = 0;
header.offset_to_a_curves = 0;
u32 offset = 2 * sizeof(u32) + sizeof(AdvancedLUTHeader);
auto advance = [&offset](BigEndian<u32>& header_slot, u32 size) {
header_slot = offset;
VERIFY(size % 4 == 0);
offset += size;
};
if (auto const& a_curves = tag_data.a_curves(); a_curves.has_value()) {
write_curves(bytes.bytes().slice(offset, a_curves_size), a_curves.value());
advance(header.offset_to_a_curves, a_curves_size);
}
if (auto const& clut = tag_data.clut(); clut.has_value()) {
write_clut(bytes.bytes().slice(offset, clut_size), clut.value());
advance(header.offset_to_clut, clut_size);
}
if (auto const& m_curves = tag_data.m_curves(); m_curves.has_value()) {
write_curves(bytes.bytes().slice(offset, m_curves_size), m_curves.value());
advance(header.offset_to_m_curves, m_curves_size);
}
if (auto const& e_matrix = tag_data.e_matrix(); e_matrix.has_value()) {
write_matrix(bytes.bytes().slice(offset, e_matrix_size), e_matrix.value());
advance(header.offset_to_matrix, e_matrix_size);
}
write_curves(bytes.bytes().slice(offset, b_curves_size), tag_data.b_curves());
advance(header.offset_to_b_curves, b_curves_size);
return bytes;
}
static ErrorOr<ByteBuffer> encode_lut_b_to_a(LutBToATagData const& tag_data)
{
// ICC v4, 10.13 lutBToAType
u32 b_curves_size = byte_size_of_curves(tag_data.b_curves());
u32 e_matrix_size = tag_data.e_matrix().has_value() ? 12 * sizeof(s15Fixed16Number) : 0;
u32 m_curves_size = tag_data.m_curves().map(byte_size_of_curves).value_or(0);
u32 clut_size = tag_data.clut().map(byte_size_of_clut).value_or(0);
u32 a_curves_size = tag_data.a_curves().map(byte_size_of_curves).value_or(0);
auto bytes = TRY(ByteBuffer::create_uninitialized(2 * sizeof(u32) + sizeof(AdvancedLUTHeader) + b_curves_size + e_matrix_size + m_curves_size + clut_size + a_curves_size));
*bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(LutBToATagData::Type);
*bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
auto& header = *bit_cast<AdvancedLUTHeader*>(bytes.data() + 8);
header.number_of_input_channels = tag_data.number_of_input_channels();
header.number_of_output_channels = tag_data.number_of_output_channels();
header.reserved_for_padding = 0;
header.offset_to_b_curves = 0;
header.offset_to_matrix = 0;
header.offset_to_m_curves = 0;
header.offset_to_clut = 0;
header.offset_to_a_curves = 0;
u32 offset = 2 * sizeof(u32) + sizeof(AdvancedLUTHeader);
auto advance = [&offset](BigEndian<u32>& header_slot, u32 size) {
header_slot = offset;
VERIFY(size % 4 == 0);
offset += size;
};
write_curves(bytes.bytes().slice(offset, b_curves_size), tag_data.b_curves());
advance(header.offset_to_b_curves, b_curves_size);
if (auto const& e_matrix = tag_data.e_matrix(); e_matrix.has_value()) {
write_matrix(bytes.bytes().slice(offset, e_matrix_size), e_matrix.value());
advance(header.offset_to_matrix, e_matrix_size);
}
if (auto const& m_curves = tag_data.m_curves(); m_curves.has_value()) {
write_curves(bytes.bytes().slice(offset, m_curves_size), m_curves.value());
advance(header.offset_to_m_curves, m_curves_size);
}
if (auto const& clut = tag_data.clut(); clut.has_value()) {
write_clut(bytes.bytes().slice(offset, clut_size), clut.value());
advance(header.offset_to_clut, clut_size);
}
if (auto const& a_curves = tag_data.a_curves(); a_curves.has_value()) {
write_curves(bytes.bytes().slice(offset, a_curves_size), a_curves.value());
advance(header.offset_to_a_curves, a_curves_size);
}
return bytes;
}
static ErrorOr<ByteBuffer> encode_measurement(MeasurementTagData const& tag_data) static ErrorOr<ByteBuffer> encode_measurement(MeasurementTagData const& tag_data)
{ {
// ICC v4, 10.14 measurementType // ICC v4, 10.14 measurementType
@ -227,10 +420,13 @@ static ErrorOr<ByteBuffer> encode_named_color_2(NamedColor2TagData const& tag_da
return bytes; return bytes;
} }
static ErrorOr<ByteBuffer> encode_parametric_curve(ParametricCurveTagData const& tag_data) static u32 parametric_curve_encoded_size(ParametricCurveTagData const& tag_data)
{
return 2 * sizeof(u32) + 2 * sizeof(u16) + tag_data.parameter_count() * sizeof(s15Fixed16Number);
}
static void encode_parametric_curve_to(ParametricCurveTagData const& tag_data, Bytes bytes)
{ {
// ICC v4, 10.18 parametricCurveType
auto bytes = TRY(ByteBuffer::create_uninitialized(2 * sizeof(u32) + 2 * sizeof(u16) + tag_data.parameter_count() * sizeof(s15Fixed16Number)));
*bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(ParametricCurveTagData::Type); *bit_cast<BigEndian<u32>*>(bytes.data()) = static_cast<u32>(ParametricCurveTagData::Type);
*bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0; *bit_cast<BigEndian<u32>*>(bytes.data() + 4) = 0;
@ -240,7 +436,13 @@ static ErrorOr<ByteBuffer> encode_parametric_curve(ParametricCurveTagData const&
auto* parameters = bit_cast<BigEndian<s15Fixed16Number>*>(bytes.data() + 12); auto* parameters = bit_cast<BigEndian<s15Fixed16Number>*>(bytes.data() + 12);
for (size_t i = 0; i < tag_data.parameter_count(); ++i) for (size_t i = 0; i < tag_data.parameter_count(); ++i)
parameters[i] = tag_data.parameter(i).raw(); parameters[i] = tag_data.parameter(i).raw();
}
static ErrorOr<ByteBuffer> encode_parametric_curve(ParametricCurveTagData const& tag_data)
{
// ICC v4, 10.18 parametricCurveType
auto bytes = TRY(ByteBuffer::create_uninitialized(parametric_curve_encoded_size(tag_data)));
encode_parametric_curve_to(tag_data, bytes.bytes());
return bytes; return bytes;
} }
@ -380,6 +582,10 @@ static ErrorOr<ByteBuffer> encode_tag_data(TagData const& tag_data)
return encode_lut_16(static_cast<Lut16TagData const&>(tag_data)); return encode_lut_16(static_cast<Lut16TagData const&>(tag_data));
case Lut8TagData::Type: case Lut8TagData::Type:
return encode_lut_8(static_cast<Lut8TagData const&>(tag_data)); return encode_lut_8(static_cast<Lut8TagData const&>(tag_data));
case LutAToBTagData::Type:
return encode_lut_a_to_b(static_cast<LutAToBTagData const&>(tag_data));
case LutBToATagData::Type:
return encode_lut_b_to_a(static_cast<LutBToATagData const&>(tag_data));
case MeasurementTagData::Type: case MeasurementTagData::Type:
return encode_measurement(static_cast<MeasurementTagData const&>(tag_data)); return encode_measurement(static_cast<MeasurementTagData const&>(tag_data));
case MultiLocalizedUnicodeTagData::Type: case MultiLocalizedUnicodeTagData::Type:

View file

@ -335,6 +335,8 @@ public:
VERIFY(!m_a_curves.has_value() || m_a_curves->size() == m_number_of_input_channels); VERIFY(!m_a_curves.has_value() || m_a_curves->size() == m_number_of_input_channels);
VERIFY(!m_m_curves.has_value() || m_m_curves->size() == m_number_of_output_channels); VERIFY(!m_m_curves.has_value() || m_m_curves->size() == m_number_of_output_channels);
VERIFY(m_b_curves.size() == m_number_of_output_channels); VERIFY(m_b_curves.size() == m_number_of_output_channels);
VERIFY(number_of_input_channels == number_of_output_channels || m_clut.has_value());
VERIFY(m_a_curves.has_value() == m_clut.has_value()); VERIFY(m_a_curves.has_value() == m_clut.has_value());
VERIFY(m_m_curves.has_value() == m_e.has_value()); VERIFY(m_m_curves.has_value() == m_e.has_value());
} }
@ -387,8 +389,10 @@ public:
VERIFY(m_b_curves.size() == m_number_of_input_channels); VERIFY(m_b_curves.size() == m_number_of_input_channels);
VERIFY(!m_m_curves.has_value() || m_m_curves->size() == m_number_of_input_channels); VERIFY(!m_m_curves.has_value() || m_m_curves->size() == m_number_of_input_channels);
VERIFY(!m_a_curves.has_value() || m_a_curves->size() == m_number_of_output_channels); VERIFY(!m_a_curves.has_value() || m_a_curves->size() == m_number_of_output_channels);
VERIFY(m_e.has_value() == m_m_curves.has_value()); VERIFY(m_e.has_value() == m_m_curves.has_value());
VERIFY(m_clut.has_value() == m_a_curves.has_value()); VERIFY(m_clut.has_value() == m_a_curves.has_value());
VERIFY(number_of_input_channels == number_of_output_channels || m_clut.has_value());
} }
u8 number_of_input_channels() const { return m_number_of_input_channels; } u8 number_of_input_channels() const { return m_number_of_input_channels; }