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:
parent
2a4d7a193e
commit
74e5fff77a
|
@ -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:");
|
||||
|
|
|
@ -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; }
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue
Block a user