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:
parent
d50b4f3581
commit
7853be7869
2 changed files with 216 additions and 6 deletions
|
@ -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:
|
||||||
|
|
|
@ -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; }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue