From 4b2e18f34fd6c6e11161daa0b986f0927f8d5307 Mon Sep 17 00:00:00 2001 From: Nico Weber Date: Fri, 10 Feb 2023 15:39:38 -0500 Subject: [PATCH] LibGfx: Add scaffolding for curves in lutAToBType and lutBToAType --- Userland/Libraries/LibGfx/ICC/TagTypes.cpp | 57 +++++++++++++--------- Userland/Libraries/LibGfx/ICC/TagTypes.h | 40 +++++++++++++-- 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp index 253fa74421..8f44b25d8d 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp @@ -358,6 +358,17 @@ static ErrorOr read_clut_data(ReadonlyBytes bytes, AdvancedLUTHeader c return CLUTData { move(number_of_grid_points_in_dimension), move(values) }; } +static ErrorOr> read_curves(ReadonlyBytes bytes, u32 offset, u32 count) +{ + Vector 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> LutAToBTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) { // ICC v4, 10.12 lutAToBType @@ -372,6 +383,7 @@ ErrorOr> 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> 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> 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 clut_data; @@ -398,9 +410,9 @@ ErrorOr> 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> 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> 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 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> LutBToATagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) @@ -444,6 +456,7 @@ ErrorOr> 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> 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 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> 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> 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 clut_data; @@ -494,12 +507,12 @@ ErrorOr> 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> 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> MeasurementTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.h b/Userland/Libraries/LibGfx/ICC/TagTypes.h index 60bd162e9f..62099b460a 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.h +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.h @@ -303,6 +303,8 @@ struct CLUTData { Variant, Vector> values; }; +using LutCurveType = NonnullRefPtr; // FIXME: Variant instead? + // ICC v4, 10.12 lutAToBType class LutAToBTagData : public TagData { public: @@ -310,32 +312,47 @@ public: static ErrorOr> from_bytes(ReadonlyBytes, u32 offset, u32 size); - LutAToBTagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, Optional clut, Optional e) + LutAToBTagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, + Optional> a_curves, Optional clut, Optional> m_curves, Optional e, Vector 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> const& a_curves() const { return m_a_curves; } Optional const& clut() const { return m_clut; } + Optional> const& m_curves() const { return m_m_curves; } Optional const& e_matrix() const { return m_e; } + Vector 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> m_a_curves; Optional m_clut; + Optional> m_m_curves; Optional m_e; + Vector m_b_curves; }; // ICC v4, 10.13 lutBToAType @@ -345,32 +362,47 @@ public: static ErrorOr> from_bytes(ReadonlyBytes, u32 offset, u32 size); - LutBToATagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, Optional e, Optional clut) + LutBToATagData(u32 offset, u32 size, u8 number_of_input_channels, u8 number_of_output_channels, + Vector b_curves, Optional e, Optional> m_curves, Optional clut, Optional> 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 const& b_curves() const { return m_b_curves; } Optional const& e_matrix() const { return m_e; } + Optional> const& m_curves() const { return m_m_curves; } Optional const& clut() const { return m_clut; } + Optional> 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 m_b_curves; Optional m_e; + Optional> m_m_curves; Optional m_clut; + Optional> m_a_curves; }; // ICC v4, 10.14 measurementType