LibGfx: Add scaffolding for curves in lutAToBType and lutBToAType

This commit is contained in:
Nico Weber 2023-02-10 15:39:38 -05:00 committed by Linus Groh
parent 743f494a9c
commit 4b2e18f34f
Notes: sideshowbarker 2024-07-17 00:59:43 +09:00
2 changed files with 71 additions and 26 deletions

View file

@ -358,6 +358,17 @@ static ErrorOr<CLUTData> read_clut_data(ReadonlyBytes bytes, AdvancedLUTHeader c
return CLUTData { move(number_of_grid_points_in_dimension), move(values) };
}
static ErrorOr<Vector<LutCurveType>> read_curves(ReadonlyBytes bytes, u32 offset, u32 count)
{
Vector<LutCurveType> curves;
// FIXME: Implement.
(void)bytes;
(void)offset;
for (u32 i = 0; i < count; ++i)
TRY(curves.try_append(adopt_ref(*new CurveTagData(0, 0, {}))));
return curves;
}
ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
{
// ICC v4, 10.12 lutAToBType
@ -372,6 +383,7 @@ ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes
return Error::from_string_literal("ICC::Profile: lutAToBType reserved_for_padding not 0");
// "Curve data elements may be shared. For example, the offsets for A, B and M curves can be identical."
// FIXME: Implement sharing curve objects when that happens. (I haven't seen it happen in practice yet.)
// 10.12.2 “A” curves
// "There are the same number of “A” curves as there are input channels. The “A” curves may only be used when
@ -379,9 +391,9 @@ ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes
// Each “A” curve is stored as an embedded curveType or a parametricCurveType (see 10.5 or 10.16). The length
// is as indicated by the convention of the respective curve type. Note that the entire tag type, including the tag
// type signature and reserved bytes, is included for each curve."
if (header.offset_to_a_curves) {
// FIXME
}
Optional<Vector<LutCurveType>> a_curves;
if (header.offset_to_a_curves)
a_curves = TRY(read_curves(bytes, header.offset_to_a_curves, header.number_of_input_channels));
// 10.12.3 CLUT
Optional<CLUTData> clut_data;
@ -398,9 +410,9 @@ ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes
// or a parametricCurveType (see 10.5 or 10.16). The length is as indicated by the convention of the respective
// curve type. Note that the entire tag type, including the tag type signature and reserved bytes, is included for
// each curve. The “M” curves may only be used when the matrix is used."
if (header.offset_to_m_curves) {
// FIXME
}
Optional<Vector<LutCurveType>> m_curves;
if (header.offset_to_m_curves)
m_curves = TRY(read_curves(bytes, header.offset_to_m_curves, header.number_of_output_channels));
// 10.12.5 Matrix
// "The matrix is organized as a 3 x 4 array. The elements appear in order from e1-e12. The matrix elements are
@ -422,12 +434,12 @@ ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes
// parametricCurveType (see 10.5 or 10.16). The length is as indicated by the convention of the respective curve
// type. Note that the entire tag type, including the tag type signature and reserved bytes, are included for each
// curve."
if (header.offset_to_b_curves) {
// FIXME
}
if (!header.offset_to_b_curves)
return Error::from_string_literal("ICC::Profile: lutAToBType without B curves");
Vector<LutCurveType> b_curves = TRY(read_curves(bytes, header.offset_to_b_curves, header.number_of_output_channels));
// FIXME: Pass curve data once it's read above.
return adopt_ref(*new LutAToBTagData(offset, size, header.number_of_input_channels, header.number_of_output_channels, move(clut_data), e));
return adopt_ref(*new LutAToBTagData(offset, size, header.number_of_input_channels, header.number_of_output_channels,
move(a_curves), move(clut_data), move(m_curves), e, move(b_curves)));
}
ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)
@ -444,6 +456,7 @@ ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes
return Error::from_string_literal("ICC::Profile: lutBToAType reserved_for_padding not 0");
// "Curve data elements may be shared. For example, the offsets for A, B and M curves may be identical."
// FIXME: Implement sharing curve objects when that happens. (I haven't seen it happen in practice yet.)
// 10.13.2 “B” curves
// "There are the same number of “B” curves as there are input channels. The curves are stored sequentially, with
@ -451,9 +464,9 @@ ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes
// or a parametricCurveType (see 10.5 or 10.16). The length is as indicated by the convention of the proper curve
// type. Note that the entire tag type, including the tag type signature and reserved bytes, is included for each
// curve."
if (header.offset_to_b_curves) {
// FIXME
}
if (!header.offset_to_b_curves)
return Error::from_string_literal("ICC::Profile: lutBToAType without B curves");
Vector<LutCurveType> b_curves = TRY(read_curves(bytes, header.offset_to_b_curves, header.number_of_input_channels));
// 10.13.3 Matrix
// "The matrix is organized as a 3 x 4 array. The elements of the matrix appear in the type in order from e1 to e12.
@ -475,9 +488,9 @@ ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes
// a parametricCurveType (see 10.5 or 10.16). The length is as indicated by the convention of the proper curve
// type. Note that the entire tag type, including the tag type signature and reserved bytes, are included for each
// curve. The “M” curves may only be used when the matrix is used."
if (header.offset_to_m_curves) {
// FIXME
}
Optional<Vector<LutCurveType>> m_curves;
if (header.offset_to_m_curves)
m_curves = TRY(read_curves(bytes, header.offset_to_m_curves, header.number_of_input_channels));
// 10.13.5 CLUT
Optional<CLUTData> clut_data;
@ -494,12 +507,12 @@ ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes
// Each “A” curve is stored as an embedded curveType or a parametricCurveType (see 10.5 or 10.16). The length
// is as indicated by the convention of the proper curve type. Note that the entire tag type, including the tag type
// signature and reserved bytes, is included for each curve."
if (header.offset_to_a_curves) {
// FIXME
}
Optional<Vector<LutCurveType>> a_curves;
if (header.offset_to_a_curves)
a_curves = TRY(read_curves(bytes, header.offset_to_a_curves, header.number_of_output_channels));
// FIXME: Pass curve data once it's read above.
return adopt_ref(*new LutBToATagData(offset, size, header.number_of_input_channels, header.number_of_output_channels, e, move(clut_data)));
return adopt_ref(*new LutBToATagData(offset, size, header.number_of_input_channels, header.number_of_output_channels,
move(b_curves), e, move(m_curves), move(clut_data), move(a_curves)));
}
ErrorOr<NonnullRefPtr<MeasurementTagData>> MeasurementTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size)

View file

@ -303,6 +303,8 @@ struct CLUTData {
Variant<Vector<u8>, Vector<u16>> values;
};
using LutCurveType = NonnullRefPtr<TagData>; // FIXME: Variant<CurveTagData, ParametricCurveTagData> instead?
// ICC v4, 10.12 lutAToBType
class LutAToBTagData : public TagData {
public:
@ -310,32 +312,47 @@ public:
static ErrorOr<NonnullRefPtr<LutAToBTagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
LutAToBTagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, Optional<CLUTData> clut, Optional<EMatrix3x4> e)
LutAToBTagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels,
Optional<Vector<LutCurveType>> a_curves, Optional<CLUTData> clut, Optional<Vector<LutCurveType>> m_curves, Optional<EMatrix3x4> e, Vector<LutCurveType> b_curves)
: TagData(offset, size, Type)
, m_number_of_input_channels(number_of_input_channels)
, m_number_of_output_channels(number_of_output_channels)
, m_a_curves(move(a_curves))
, m_clut(move(clut))
, m_m_curves(move(m_curves))
, m_e(e)
, m_b_curves(move(b_curves))
{
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_b_curves.size() == m_number_of_output_channels);
}
u8 number_of_input_channels() const { return m_number_of_input_channels; }
u8 number_of_output_channels() const { return m_number_of_output_channels; }
Optional<Vector<LutCurveType>> const& a_curves() const { return m_a_curves; }
Optional<CLUTData> const& clut() const { return m_clut; }
Optional<Vector<LutCurveType>> const& m_curves() const { return m_m_curves; }
Optional<EMatrix3x4> const& e_matrix() const { return m_e; }
Vector<LutCurveType> const& b_curves() const { return m_b_curves; }
private:
u8 m_number_of_input_channels;
u8 m_number_of_output_channels;
// "Only the following combinations are permitted:
// "It is possible to use any or all of these processing elements. At least one processing element shall be included.
// Only the following combinations are permitted:
// - B;
// - M, Matrix, B;
// - A, CLUT, B;
// - A, CLUT, M, Matrix, B."
// This seems to imply that the B curve is not in fact optional.
Optional<Vector<LutCurveType>> m_a_curves;
Optional<CLUTData> m_clut;
Optional<Vector<LutCurveType>> m_m_curves;
Optional<EMatrix3x4> m_e;
Vector<LutCurveType> m_b_curves;
};
// ICC v4, 10.13 lutBToAType
@ -345,32 +362,47 @@ public:
static ErrorOr<NonnullRefPtr<LutBToATagData>> from_bytes(ReadonlyBytes, u32 offset, u32 size);
LutBToATagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, Optional<EMatrix3x4> e, Optional<CLUTData> clut)
LutBToATagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels,
Vector<LutCurveType> b_curves, Optional<EMatrix3x4> e, Optional<Vector<LutCurveType>> m_curves, Optional<CLUTData> clut, Optional<Vector<LutCurveType>> a_curves)
: TagData(offset, size, Type)
, m_number_of_input_channels(number_of_input_channels)
, m_number_of_output_channels(number_of_output_channels)
, m_b_curves(move(b_curves))
, m_e(e)
, m_m_curves(move(m_curves))
, m_clut(move(clut))
, m_a_curves(move(a_curves))
{
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_a_curves.has_value() || m_a_curves->size() == m_number_of_output_channels);
}
u8 number_of_input_channels() const { return m_number_of_input_channels; }
u8 number_of_output_channels() const { return m_number_of_output_channels; }
Vector<LutCurveType> const& b_curves() const { return m_b_curves; }
Optional<EMatrix3x4> const& e_matrix() const { return m_e; }
Optional<Vector<LutCurveType>> const& m_curves() const { return m_m_curves; }
Optional<CLUTData> const& clut() const { return m_clut; }
Optional<Vector<LutCurveType>> const& a_curves() const { return m_a_curves; }
private:
u8 m_number_of_input_channels;
u8 m_number_of_output_channels;
// "Only the following combinations are permitted:
// "It is possible to use any or all of these processing elements. At least one processing element shall be included.
// Only the following combinations are permitted:
// - B;
// - B, Matrix, M;
// - B, CLUT, A;
// - B, Matrix, M, CLUT, A."
// This seems to imply that the B curve is not in fact optional.
Vector<LutCurveType> m_b_curves;
Optional<EMatrix3x4> m_e;
Optional<Vector<LutCurveType>> m_m_curves;
Optional<CLUTData> m_clut;
Optional<Vector<LutCurveType>> m_a_curves;
};
// ICC v4, 10.14 measurementType