From 74e5fff77a9fbf29d88319704272781fca43d66c Mon Sep 17 00:00:00 2001 From: Sam Atkins Date: Thu, 2 Nov 2023 12:41:25 +0000 Subject: [PATCH] LibGfx: Reduce bit casting in OpenType GPOS table after construction Read the basic lists as spans, and use those when looking for kerning. Kerning lookup still does bit-casting for now. As for CBLC, the data is a bit complicated. --- .../Libraries/LibGfx/Font/OpenType/Tables.cpp | 46 +++++++------ .../Libraries/LibGfx/Font/OpenType/Tables.h | 68 +++++++++++++++++-- 2 files changed, 91 insertions(+), 23 deletions(-) diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp b/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp index b5273d8b32..52be013272 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp +++ b/Userland/Libraries/LibGfx/Font/OpenType/Tables.cpp @@ -509,6 +509,27 @@ ErrorOr CBDT::from_slice(ReadonlyBytes slice) return CBDT { slice }; } +ErrorOr GPOS::from_slice(ReadonlyBytes slice) +{ + FixedMemoryStream stream { slice }; + auto const& header = *TRY(stream.read_in_place()); + // FIXME: Detect version 1.1 and support the extra FeatureVariations table. + + TRY(stream.seek(header.script_list_offset, SeekMode::SetPosition)); + auto const& script_list = *TRY(stream.read_in_place()); + auto script_records = TRY(stream.read_in_place(script_list.script_count)); + + TRY(stream.seek(header.feature_list_offset, SeekMode::SetPosition)); + auto const& feature_list = *TRY(stream.read_in_place()); + auto feature_records = TRY(stream.read_in_place(feature_list.feature_count)); + + TRY(stream.seek(header.lookup_list_offset, SeekMode::SetPosition)); + auto const& lookup_list = *TRY(stream.read_in_place()); + auto lookup_records = TRY(stream.read_in_place(lookup_list.lookup_count)); + + return GPOS { slice, header, script_list, script_records, feature_list, feature_records, lookup_list, lookup_records }; +} + Optional GPOS::glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const { auto read_value_record = [&](u16 value_format, FixedMemoryStream& stream) -> ValueRecord { @@ -532,30 +553,17 @@ Optional GPOS::glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const return value_record; }; - auto const& header = this->header(); dbgln_if(OPENTYPE_GPOS_DEBUG, "GPOS header:"); - dbgln_if(OPENTYPE_GPOS_DEBUG, " Version: {}.{}", header.major_version, header.minor_version); - dbgln_if(OPENTYPE_GPOS_DEBUG, " Feature list offset: {}", header.feature_list_offset); + dbgln_if(OPENTYPE_GPOS_DEBUG, " Version: {}.{}", m_header.major_version, m_header.minor_version); + dbgln_if(OPENTYPE_GPOS_DEBUG, " Feature list offset: {}", m_header.feature_list_offset); // FIXME: Make sure everything is bounds-checked appropriately. - auto feature_list_slice = m_slice.slice(header.feature_list_offset); - if (feature_list_slice.size() < sizeof(FeatureList)) { - dbgln_if(OPENTYPE_GPOS_DEBUG, "GPOS table feature list slice is too small"); - return {}; - } - auto const& feature_list = *bit_cast(feature_list_slice.data()); - - auto lookup_list_slice = m_slice.slice(header.lookup_list_offset); - if (lookup_list_slice.size() < sizeof(LookupList)) { - dbgln_if(OPENTYPE_GPOS_DEBUG, "GPOS table lookup list slice is too small"); - return {}; - } - auto const& lookup_list = *bit_cast(lookup_list_slice.data()); + auto feature_list_slice = m_slice.slice(m_header.feature_list_offset); + auto lookup_list_slice = m_slice.slice(m_header.lookup_list_offset); Optional kern_feature_offset; - for (size_t i = 0; i < feature_list.feature_count; ++i) { - auto const& feature_record = feature_list.feature_records[i]; + for (auto const& feature_record : m_feature_records) { if (feature_record.feature_tag == tag_from_str("kern")) { kern_feature_offset = feature_record.feature_offset; break; @@ -577,7 +585,7 @@ Optional GPOS::glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const for (size_t i = 0; i < feature.lookup_index_count; ++i) { auto lookup_index = feature.lookup_list_indices[i]; dbgln_if(OPENTYPE_GPOS_DEBUG, "Lookup index: {}", lookup_index); - auto lookup_slice = lookup_list_slice.slice(lookup_list.lookup_offsets[lookup_index]); + auto lookup_slice = lookup_list_slice.slice(m_lookup_offsets[lookup_index]); auto const& lookup = *bit_cast(lookup_slice.data()); dbgln_if(OPENTYPE_GPOS_DEBUG, "Lookup:"); diff --git a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h index db2ae9e016..f7a83a339f 100644 --- a/Userland/Libraries/LibGfx/Font/OpenType/Tables.h +++ b/Userland/Libraries/LibGfx/Font/OpenType/Tables.h @@ -675,6 +675,20 @@ struct [[gnu::packed]] ClassDefFormat2 { }; static_assert(AssertSize()); +// https://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record +struct [[gnu::packed]] ScriptRecord { + Tag script_tag; + Offset16 script_offset; +}; +static_assert(AssertSize()); + +// https://learn.microsoft.com/en-us/typography/opentype/spec/chapter2#script-list-table-and-script-record +struct [[gnu::packed]] ScriptList { + Uint16 script_count; + ScriptRecord script_records[]; +}; +static_assert(AssertSize()); + // https://learn.microsoft.com/en-us/typography/opentype/spec/gpos#gpos-header class GPOS { public: @@ -736,19 +750,37 @@ public: Y_ADVANCE_DEVICE = 0x0080, }; - Version1_0 const& header() const { return *bit_cast(m_slice.data()); } - Optional glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const; - static ErrorOr from_slice(ReadonlyBytes slice) { return GPOS { slice }; } + static ErrorOr from_slice(ReadonlyBytes); private: - GPOS(ReadonlyBytes slice) + GPOS(ReadonlyBytes slice, Version1_0 const& header, + ScriptList const& script_list, ReadonlySpan script_records, + FeatureList const& feature_list, ReadonlySpan feature_records, + LookupList const& lookup_list, ReadonlySpan lookup_offsets) : m_slice(slice) + , m_header(header) + , m_script_list(script_list) + , m_script_records(script_records) + , m_feature_list(feature_list) + , m_feature_records(feature_records) + , m_lookup_list(lookup_list) + , m_lookup_offsets(lookup_offsets) { } ReadonlyBytes m_slice; + Version1_0 const& m_header; + + ScriptList const& m_script_list; + ReadonlySpan m_script_records; + + FeatureList const& m_feature_list; + ReadonlySpan m_feature_records; + + LookupList const& m_lookup_list; + ReadonlySpan m_lookup_offsets; }; } @@ -769,4 +801,32 @@ template<> struct Traits : public GenericTraits { static constexpr bool is_trivially_serializable() { return true; } }; + +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; + +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; +template<> +struct Traits : public GenericTraits { + static constexpr bool is_trivially_serializable() { return true; } +}; }