1
0
mirror of https://github.com/SerenityOS/serenity synced 2024-07-09 14:10:45 +00:00

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.
This commit is contained in:
Sam Atkins 2023-11-02 12:41:25 +00:00 committed by Andreas Kling
parent 2a4d7a193e
commit 74e5fff77a
2 changed files with 91 additions and 23 deletions

View File

@ -509,6 +509,27 @@ ErrorOr<CBDT> CBDT::from_slice(ReadonlyBytes slice)
return CBDT { slice };
}
ErrorOr<GPOS> GPOS::from_slice(ReadonlyBytes slice)
{
FixedMemoryStream stream { slice };
auto const& header = *TRY(stream.read_in_place<Version1_0 const>());
// 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<ScriptList const>());
auto script_records = TRY(stream.read_in_place<ScriptRecord const>(script_list.script_count));
TRY(stream.seek(header.feature_list_offset, SeekMode::SetPosition));
auto const& feature_list = *TRY(stream.read_in_place<FeatureList const>());
auto feature_records = TRY(stream.read_in_place<FeatureRecord const>(feature_list.feature_count));
TRY(stream.seek(header.lookup_list_offset, SeekMode::SetPosition));
auto const& lookup_list = *TRY(stream.read_in_place<LookupList const>());
auto lookup_records = TRY(stream.read_in_place<Offset16>(lookup_list.lookup_count));
return GPOS { slice, header, script_list, script_records, feature_list, feature_records, lookup_list, lookup_records };
}
Optional<i16> 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<i16> 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<FeatureList const*>(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<LookupList const*>(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<Offset16> 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<i16> 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 const*>(lookup_slice.data());
dbgln_if(OPENTYPE_GPOS_DEBUG, "Lookup:");

View File

@ -675,6 +675,20 @@ struct [[gnu::packed]] ClassDefFormat2 {
};
static_assert(AssertSize<ClassDefFormat2, 4>());
// 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<ScriptRecord, 6>());
// 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<ScriptList, 2>());
// 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<Version1_0 const*>(m_slice.data()); }
Optional<i16> glyph_kerning(u16 left_glyph_id, u16 right_glyph_id) const;
static ErrorOr<GPOS> from_slice(ReadonlyBytes slice) { return GPOS { slice }; }
static ErrorOr<GPOS> from_slice(ReadonlyBytes);
private:
GPOS(ReadonlyBytes slice)
GPOS(ReadonlyBytes slice, Version1_0 const& header,
ScriptList const& script_list, ReadonlySpan<ScriptRecord> script_records,
FeatureList const& feature_list, ReadonlySpan<FeatureRecord> feature_records,
LookupList const& lookup_list, ReadonlySpan<Offset16> 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<ScriptRecord> m_script_records;
FeatureList const& m_feature_list;
ReadonlySpan<FeatureRecord> m_feature_records;
LookupList const& m_lookup_list;
ReadonlySpan<Offset16> m_lookup_offsets;
};
}
@ -769,4 +801,32 @@ template<>
struct Traits<OpenType::Kern::Format0Pair const> : public GenericTraits<OpenType::Kern::Format0Pair const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::GPOS::Version1_0 const> : public GenericTraits<OpenType::GPOS::Version1_0 const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::FeatureList const> : public GenericTraits<OpenType::FeatureList const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::FeatureRecord const> : public GenericTraits<OpenType::FeatureRecord const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::LookupList const> : public GenericTraits<OpenType::LookupList const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::ScriptList const> : public GenericTraits<OpenType::ScriptList const> {
static constexpr bool is_trivially_serializable() { return true; }
};
template<>
struct Traits<OpenType::ScriptRecord const> : public GenericTraits<OpenType::ScriptRecord const> {
static constexpr bool is_trivially_serializable() { return true; }
};
}