diff options
Diffstat (limited to 'Userland/Libraries/LibGfx/ICC')
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/Profile.cpp | 4 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/TagTypes.cpp | 226 | ||||
-rw-r--r-- | Userland/Libraries/LibGfx/ICC/TagTypes.h | 81 |
3 files changed, 309 insertions, 2 deletions
diff --git a/Userland/Libraries/LibGfx/ICC/Profile.cpp b/Userland/Libraries/LibGfx/ICC/Profile.cpp index 9a37d49357..f7d1537db7 100644 --- a/Userland/Libraries/LibGfx/ICC/Profile.cpp +++ b/Userland/Libraries/LibGfx/ICC/Profile.cpp @@ -580,6 +580,10 @@ ErrorOr<NonnullRefPtr<TagData>> Profile::read_tag(ReadonlyBytes bytes, u32 offse 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 LutAToBTagData::Type: + return LutAToBTagData::from_bytes(tag_bytes, offset_to_beginning_of_tag_data_element, size_of_tag_data_element); + case LutBToATagData::Type: + return LutBToATagData::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 NamedColor2TagData::Type: diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp index c7a7635093..8bbcb4f762 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.cpp +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.cpp @@ -38,6 +38,29 @@ struct LUTHeader { }; static_assert(AssertSize<LUTHeader, 40>()); +// Common bits of ICC v4, Table 45 — lutAToBType encoding and Table 47 — lutBToAType encoding +struct AdvancedLUTHeader { + u8 number_of_input_channels; + u8 number_of_output_channels; + BigEndian<u16> reserved_for_padding; + BigEndian<u32> offset_to_b_curves; + BigEndian<u32> offset_to_matrix; + BigEndian<u32> offset_to_m_curves; + BigEndian<u32> offset_to_clut; + BigEndian<u32> offset_to_a_curves; +}; +static_assert(AssertSize<AdvancedLUTHeader, 24>()); + +// ICC v4, Table 46 — lutAToBType CLUT encoding +// ICC v4, Table 48 — lutBToAType CLUT encoding +// (They're identical.) +struct CLUTHeader { + u8 number_of_grid_points_in_dimension[16]; + u8 precision_of_data_elements; // 1 for u8 entries, 2 for u16 entries. + u8 reserved_for_padding[3]; +}; +static_assert(AssertSize<CLUTHeader, 20>()); + ErrorOr<void> check_reserved(ReadonlyBytes tag_bytes) { if (tag_bytes.size() < 2 * sizeof(u32)) @@ -206,6 +229,209 @@ ErrorOr<NonnullRefPtr<Lut8TagData>> Lut8TagData::from_bytes(ReadonlyBytes bytes, move(input_tables), move(clut_values), move(output_tables))); } +static ErrorOr<CLUTData> read_clut_data(ReadonlyBytes bytes, AdvancedLUTHeader const& header) +{ + // Reads a CLUT as described in ICC v4, 10.12.3 CLUT and 10.13.5 CLUT (the two sections are virtually identical). + if (header.offset_to_clut + sizeof(CLUTHeader) > bytes.size()) + return Error::from_string_literal("ICC::Profile: clut out of bounds"); + + if (header.number_of_input_channels >= sizeof(CLUTHeader::number_of_grid_points_in_dimension)) + return Error::from_string_literal("ICC::Profile: clut has too many input channels"); + + auto& clut_header = *bit_cast<CLUTHeader const*>(bytes.data() + header.offset_to_clut); + + // "Number of grid points in each dimension. Only the first i entries are used, where i is the number of input channels." + Vector<u8, 4> number_of_grid_points_in_dimension; + TRY(number_of_grid_points_in_dimension.try_resize(header.number_of_input_channels)); + for (size_t i = 0; i < header.number_of_input_channels; ++i) + number_of_grid_points_in_dimension[i] = clut_header.number_of_grid_points_in_dimension[i]; + + // "Unused entries shall be set to 00h." + for (size_t i = header.number_of_input_channels; i < sizeof(CLUTHeader::number_of_grid_points_in_dimension); ++i) { + if (clut_header.number_of_grid_points_in_dimension[i] != 0) + return Error::from_string_literal("ICC::Profile: unused clut grid point not 0"); + } + + // "Precision of data elements in bytes. Shall be either 01h or 02h." + if (clut_header.precision_of_data_elements != 1 && clut_header.precision_of_data_elements != 2) + return Error::from_string_literal("ICC::Profile: clut invalid data element precision"); + + // "Reserved for padding, shall be set to 0" + for (size_t i = 0; i < sizeof(CLUTHeader::reserved_for_padding); ++i) { + if (clut_header.reserved_for_padding[i] != 0) + return Error::from_string_literal("ICC::Profile: clut reserved for padding not 0"); + } + + // "The size of the CLUT in bytes is (nGrid1 x nGrid2 x…x nGridN) x number of output channels (o) x size of (channel component)." + u32 clut_size = header.number_of_output_channels; + for (u8 grid_size_in_dimension : number_of_grid_points_in_dimension) + clut_size *= grid_size_in_dimension; + + if (header.offset_to_clut + sizeof(CLUTHeader) + clut_size * clut_header.precision_of_data_elements > bytes.size()) + return Error::from_string_literal("ICC::Profile: clut data out of bounds"); + + if (clut_header.precision_of_data_elements == 1) { + auto* raw_values = bytes.data() + header.offset_to_clut + sizeof(CLUTHeader); + Vector<u8> values; + TRY(values.try_resize(clut_size)); + for (u32 i = 0; i < clut_size; ++i) + values[i] = raw_values[i]; + return CLUTData { move(number_of_grid_points_in_dimension), move(values) }; + } + + VERIFY(clut_header.precision_of_data_elements == 2); + auto* raw_values = bit_cast<BigEndian<u16> const*>(bytes.data() + header.offset_to_clut + sizeof(CLUTHeader)); + Vector<u16> values; + TRY(values.try_resize(clut_size)); + for (u32 i = 0; i < clut_size; ++i) + values[i] = raw_values[i]; + return CLUTData { move(number_of_grid_points_in_dimension), move(values) }; +} + +ErrorOr<NonnullRefPtr<LutAToBTagData>> LutAToBTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) +{ + // ICC v4, 10.12 lutAToBType + VERIFY(tag_type(bytes) == Type); + TRY(check_reserved(bytes)); + + if (bytes.size() < 2 * sizeof(u32) + sizeof(AdvancedLUTHeader)) + return Error::from_string_literal("ICC::Profile: lutAToBType has not enough data"); + + auto& header = *bit_cast<AdvancedLUTHeader const*>(bytes.data() + 8); + if (header.reserved_for_padding != 0) + 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." + + // 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 + // the CLUT is used. The curves are stored sequentially, with 00h bytes used for padding between them if needed. + // 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 + } + + // 10.12.3 CLUT + Optional<CLUTData> clut_data; + if (header.offset_to_clut) { + clut_data = TRY(read_clut_data(bytes, header)); + } else if (header.number_of_input_channels != header.number_of_output_channels) { + // "If the number of input channels does not equal the number of output channels, the CLUT shall be present." + return Error::from_string_literal("ICC::Profile: lutAToBType no CLUT despite different number of input and output channels"); + } + + // 10.12.4 “M” curves + // "There are the same number of “M” curves as there are output channels. The curves are stored sequentially, + // with 00h bytes used for padding between them if needed. Each “M” 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. The “M” curves may only be used when the matrix is used." + if (header.offset_to_m_curves) { + // FIXME + } + + // 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 + // each s15Fixed16Numbers." + Optional<EMatrix3x4> e; + if (header.offset_to_matrix) { + if (header.offset_to_matrix + 12 * sizeof(s15Fixed16Number) > bytes.size()) + return Error::from_string_literal("ICC::Profile: lutAToBType matrix out of bounds"); + + e = EMatrix3x4 {}; + auto* raw_e = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + header.offset_to_matrix); + for (int i = 0; i < 12; ++i) + e->e[i] = S15Fixed16::create_raw(raw_e[i]); + } + + // 10.12.6 “B” curves + // "There are the same number of “B” curves as there are output channels. The curves are stored sequentially, with + // 00h bytes used for padding between them if needed. Each “B” 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, are included for each + // curve." + if (header.offset_to_b_curves) { + // FIXME + } + + // 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)); +} + +ErrorOr<NonnullRefPtr<LutBToATagData>> LutBToATagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) +{ + // ICC v4, 10.13 lutBToAType + VERIFY(tag_type(bytes) == Type); + TRY(check_reserved(bytes)); + + if (bytes.size() < 2 * sizeof(u32) + sizeof(AdvancedLUTHeader)) + return Error::from_string_literal("ICC::Profile: lutBToAType has not enough data"); + + auto& header = *bit_cast<AdvancedLUTHeader const*>(bytes.data() + 8); + if (header.reserved_for_padding != 0) + 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." + + // 10.13.2 “B” curves + // "There are the same number of “B” curves as there are input channels. The curves are stored sequentially, with + // 00h bytes used for padding between them if needed. Each “B” curve is stored as an embedded curveType tag + // 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 + } + + // 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. + // The matrix elements are each s15Fixed16Numbers" + Optional<EMatrix3x4> e; + if (header.offset_to_matrix) { + if (header.offset_to_matrix + 12 * sizeof(s15Fixed16Number) > bytes.size()) + return Error::from_string_literal("ICC::Profile: lutBToAType matrix out of bounds"); + + e = EMatrix3x4 {}; + auto* raw_e = bit_cast<BigEndian<s15Fixed16Number> const*>(bytes.data() + header.offset_to_matrix); + for (int i = 0; i < 12; ++i) + e->e[i] = S15Fixed16::create_raw(raw_e[i]); + } + + // 10.13.4 “M” curves + // "There are the same number of “M” curves as there are input channels. The curves are stored sequentially, with + // 00h bytes used for padding between them if needed. Each “M” 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, are included for each + // curve. The “M” curves may only be used when the matrix is used." + if (header.offset_to_m_curves) { + // FIXME + } + + // 10.13.5 CLUT + Optional<CLUTData> clut_data; + if (header.offset_to_clut) { + clut_data = TRY(read_clut_data(bytes, header)); + } else if (header.number_of_input_channels != header.number_of_output_channels) { + // "If the number of input channels does not equal the number of output channels, the CLUT shall be present." + return Error::from_string_literal("ICC::Profile: lutAToBType no CLUT despite different number of input and output channels"); + } + + // 10.13.6 “A” curves + // "There are the same number of “A” curves as there are output channels. The “A” curves may only be used when + // the CLUT is used. The curves are stored sequentially, with 00h bytes used for padding between them if needed. + // 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 + } + + // 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))); +} + ErrorOr<NonnullRefPtr<MultiLocalizedUnicodeTagData>> MultiLocalizedUnicodeTagData::from_bytes(ReadonlyBytes bytes, u32 offset, u32 size) { // ICC v4, 10.15 multiLocalizedUnicodeType diff --git a/Userland/Libraries/LibGfx/ICC/TagTypes.h b/Userland/Libraries/LibGfx/ICC/TagTypes.h index fc4c1b0060..7096643fd6 100644 --- a/Userland/Libraries/LibGfx/ICC/TagTypes.h +++ b/Userland/Libraries/LibGfx/ICC/TagTypes.h @@ -122,6 +122,10 @@ private: }; struct EMatrix3x3 { + // A row-major 3x3 matrix: + // [ e[0] e[1] e[2] ] + // [ e[3] e[4] e[5] ] * v + // ] e[6] e[7] e[8] ] S15Fixed16 e[9]; S15Fixed16 const& operator[](unsigned i) const @@ -239,12 +243,58 @@ private: Vector<u8> m_output_tables; }; +struct EMatrix3x4 { + // A row-major 3x3 matrix followed by a translation vector: + // [ e[0] e[1] e[2] ] [ e[9] ] + // [ e[3] e[4] e[5] ] * v + [ e[10] ] + // [ e[6] e[7] e[8] ] [ e[11] ] + S15Fixed16 e[12]; + + S15Fixed16 const& operator[](unsigned i) const + { + VERIFY(i < array_size(e)); + return e[i]; + } +}; + +struct CLUTData { + Vector<u8, 4> number_of_grid_points_in_dimension; + Variant<Vector<u8>, Vector<u16>> values; +}; + // ICC v4, 10.12 lutAToBType class LutAToBTagData : public TagData { public: static constexpr TagTypeSignature Type { 0x6D414220 }; // 'mAB ' - // FIXME: Implement! + 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) + : TagData(offset, size, Type) + , m_number_of_input_channels(number_of_input_channels) + , m_number_of_output_channels(number_of_output_channels) + , m_clut(move(clut)) + , m_e(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; } + + Optional<CLUTData> const& clut() const { return m_clut; } + Optional<EMatrix3x4> const& e_matrix() const { return m_e; } + +private: + u8 m_number_of_input_channels; + u8 m_number_of_output_channels; + + // "Only the following combinations are permitted: + // - B; + // - M, Matrix, B; + // - A, CLUT, B; + // - A, CLUT, M, Matrix, B." + Optional<CLUTData> m_clut; + Optional<EMatrix3x4> m_e; }; // ICC v4, 10.13 lutBToAType @@ -252,7 +302,34 @@ class LutBToATagData : public TagData { public: static constexpr TagTypeSignature Type { 0x6D424120 }; // 'mBA ' - // FIXME: Implement! + 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) + : TagData(offset, size, Type) + , m_number_of_input_channels(number_of_input_channels) + , m_number_of_output_channels(number_of_output_channels) + , m_e(e) + , m_clut(move(clut)) + { + } + + 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<EMatrix3x4> const& e_matrix() const { return m_e; } + Optional<CLUTData> const& clut() const { return m_clut; } + +private: + u8 m_number_of_input_channels; + u8 m_number_of_output_channels; + + // "Only the following combinations are permitted: + // - B; + // - B, Matrix, M; + // - B, CLUT, A; + // - B, Matrix, M, CLUT, A." + Optional<EMatrix3x4> m_e; + Optional<CLUTData> m_clut; }; // ICC v4, 10.15 multiLocalizedUnicodeType |