[aat] Allow disable feature ranges

Fixes https://github.com/harfbuzz/harfbuzz/issues/4020

This is a hack.

To implement this properly we need to treat runs with different features
as independent runs for running the state machine, as the subtable flags
might be different. That would be a significant change to our internal
implementation.
This commit is contained in:
Behdad Esfahbod 2023-01-09 17:26:08 -07:00
parent b20871322f
commit 24a4d397ba
8 changed files with 69 additions and 34 deletions

View File

@ -740,7 +740,7 @@ struct StateTableDriver
num_glyphs (face_->get_num_glyphs ()) {}
template <typename context_t>
void drive (context_t *c)
void drive (context_t *c, hb_mask_t mask)
{
if (!c->in_place)
buffer->clear_output ();
@ -753,7 +753,7 @@ struct StateTableDriver
(unsigned) StateTableT::CLASS_END_OF_TEXT;
DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
const EntryT &entry = machine.get_entry (state, klass);
const int next_state = machine.new_state (entry.newState);
int next_state = machine.new_state (entry.newState);
/* Conditions under which it's guaranteed safe-to-break before current glyph:
*
@ -822,7 +822,10 @@ struct StateTableDriver
if (!safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
c->transition (this, entry);
if (buffer->cur().mask & mask)
c->transition (this, entry);
else
next_state = StateTableT::STATE_START_OF_TEXT;
state = next_state;
DEBUG_MSG (APPLY, nullptr, "s%d", state);
@ -864,9 +867,11 @@ struct hb_aat_apply_context_t :
const ankr *ankr_table;
const OT::GDEF *gdef_table;
hb_mask_t mask;
/* Unused. For debug tracing only. */
unsigned int lookup_index;
HB_INTERNAL hb_aat_apply_context_t (const hb_ot_shape_plan_t *plan_,
hb_font_t *font_,
hb_buffer_t *buffer_,
@ -877,6 +882,7 @@ struct hb_aat_apply_context_t :
HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
void set_lookup_index (unsigned int i) { lookup_index = i; }
void set_mask (hb_mask_t mask_) { mask = mask_; }
};

View File

@ -350,7 +350,7 @@ struct KerxSubTableFormat1
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (true);
}
@ -594,7 +594,7 @@ struct KerxSubTableFormat4
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->font->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (true);
}

View File

@ -169,7 +169,7 @@ struct RearrangementSubtable
driver_context_t dc (this);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (dc.ret);
}
@ -325,7 +325,7 @@ struct ContextualSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (dc.ret);
}
@ -577,7 +577,7 @@ struct LigatureSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (dc.ret);
}
@ -820,7 +820,7 @@ struct InsertionSubtable
driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->buffer, c->face);
driver.drive (&dc);
driver.drive (&dc, c->mask);
return_trace (dc.ret);
}
@ -954,9 +954,11 @@ struct Chain
{
typedef typename Types::HBUINT HBUINT;
hb_mask_t compile_flags (const hb_aat_map_builder_t *map) const
hb_aat_map_t::chain_info_t compile_info (const hb_aat_map_builder_t *map,
const hb_ot_map_t *ot_map) const
{
hb_mask_t flags = defaultFlags;
hb_mask_t mask = ot_map->get_global_mask ();
{
unsigned int count = featureCount;
for (unsigned i = 0; i < count; i++)
@ -967,9 +969,11 @@ struct Chain
retry:
// Check whether this type/setting pair was requested in the map, and if so, apply its flags.
// (The search here only looks at the type and setting fields of feature_info_t.)
hb_aat_map_builder_t::feature_info_t info = { type, setting, false, 0 };
if (map->features.bsearch (info))
hb_aat_map_builder_t::feature_info_t info = { HB_TAG_NONE, type, setting, false, 0 };
auto *found = map->features.bsearch (info);
if (found)
{
mask = ot_map->get_mask (found->tag);
flags &= feature.disableFlags;
flags |= feature.enableFlags;
}
@ -991,11 +995,14 @@ struct Chain
#endif
}
}
return flags;
hb_aat_map_t::chain_info_t info;
info.flags = flags;
info.mask = mask;
return info;
}
void apply (hb_aat_apply_context_t *c,
hb_mask_t flags) const
hb_aat_map_t::chain_info_t info) const
{
const ChainSubtable<Types> *subtable = &StructAfter<ChainSubtable<Types>> (featureZ.as_array (featureCount));
unsigned int count = subtableCount;
@ -1003,8 +1010,8 @@ struct Chain
{
bool reverse;
if (!(subtable->subFeatureFlags & flags))
goto skip;
//if (!(subtable->subFeatureFlags & info.flags))
//goto skip;
if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) &&
HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
@ -1049,6 +1056,8 @@ struct Chain
if (reverse)
c->buffer->reverse ();
c->set_mask (info.mask);
subtable->apply (c);
if (reverse)
@ -1115,14 +1124,15 @@ struct mortmorx
bool has_data () const { return version != 0; }
void compile_flags (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map) const
void compile_info (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map,
const hb_ot_map_t *ot_map) const
{
const Chain<Types> *chain = &firstChain;
unsigned int count = chainCount;
for (unsigned int i = 0; i < count; i++)
{
map->chain_flags.push (chain->compile_flags (mapper));
map->chain_info.push (chain->compile_info (mapper, ot_map));
chain = &StructAfter<Chain<Types>> (*chain);
}
}
@ -1135,7 +1145,7 @@ struct mortmorx
unsigned int count = chainCount;
for (unsigned int i = 0; i < count; i++)
{
chain->apply (c, c->plan->aat_map.chain_flags[i]);
chain->apply (c, c->plan->aat_map.chain_info[i]);
if (unlikely (!c->buffer->successful)) return;
chain = &StructAfter<Chain<Types>> (*chain);
}

View File

@ -203,19 +203,20 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
void
hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map)
hb_aat_map_t *map,
const hb_ot_map_t *ot_map)
{
const AAT::morx& morx = *mapper->face->table.morx;
if (morx.has_data ())
{
morx.compile_flags (mapper, map);
morx.compile_info (mapper, map, ot_map);
return;
}
const AAT::mort& mort = *mapper->face->table.mort;
if (mort.has_data ())
{
mort.compile_flags (mapper, map);
mort.compile_info (mapper, map, ot_map);
return;
}
}

View File

@ -48,7 +48,8 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag);
HB_INTERNAL void
hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map);
hb_aat_map_t *map,
const hb_ot_map_t *ot_map);
HB_INTERNAL void
hb_aat_layout_substitute (const hb_ot_shape_plan_t *plan,

View File

@ -45,6 +45,7 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
if (!face->table.feat->exposes_feature (HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES))
return;
feature_info_t *info = features.push();
info->tag = tag;
info->type = HB_AAT_LAYOUT_FEATURE_TYPE_CHARACTER_ALTERNATIVES;
info->setting = (hb_aat_layout_feature_selector_t) value;
info->seq = features.length;
@ -58,7 +59,7 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
const AAT::FeatureName* feature = &face->table.feat->get_feature (mapping->aatFeatureType);
if (!feature->has_data ())
{
/* Special case: Chain::compile_flags will fall back to the deprecated version of
/* Special case: Chain::compile_info will fall back to the deprecated version of
* small-caps if necessary, so we need to check for that possibility.
* https://github.com/harfbuzz/harfbuzz/issues/2307 */
if (mapping->aatFeatureType == HB_AAT_LAYOUT_FEATURE_TYPE_LOWER_CASE &&
@ -71,6 +72,7 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
}
feature_info_t *info = features.push();
info->tag = tag;
info->type = mapping->aatFeatureType;
info->setting = value ? mapping->selectorToEnable : mapping->selectorToDisable;
info->seq = features.length;
@ -78,7 +80,7 @@ void hb_aat_map_builder_t::add_feature (hb_tag_t tag, unsigned value)
}
void
hb_aat_map_builder_t::compile (hb_aat_map_t &m)
hb_aat_map_builder_t::compile (hb_aat_map_t &m, const hb_ot_map_t &ot_m)
{
/* Sort features and merge duplicates */
if (features.length)
@ -95,7 +97,7 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
features.shrink (j + 1);
}
hb_aat_layout_compile_map (this, &m);
hb_aat_layout_compile_map (this, &m, &ot_m);
}

View File

@ -29,6 +29,8 @@
#include "hb.hh"
#include "hb-ot-map.hh"
struct hb_aat_map_t
{
@ -36,15 +38,21 @@ struct hb_aat_map_t
public:
struct chain_info_t
{
hb_mask_t flags;
hb_mask_t mask;
};
void init ()
{
hb_memset (this, 0, sizeof (*this));
chain_flags.init ();
chain_info.init ();
}
void fini () { chain_flags.fini (); }
void fini () { chain_info.fini (); }
public:
hb_vector_t<hb_mask_t> chain_flags;
hb_vector_t<chain_info_t> chain_info;
};
struct hb_aat_map_builder_t
@ -58,11 +66,12 @@ struct hb_aat_map_builder_t
HB_INTERNAL void add_feature (hb_tag_t tag, unsigned int value=1);
HB_INTERNAL void compile (hb_aat_map_t &m);
HB_INTERNAL void compile (hb_aat_map_t &m, const hb_ot_map_t &ot_m);
public:
struct feature_info_t
{
hb_tag_t tag;
hb_aat_layout_feature_type_t type;
hb_aat_layout_feature_selector_t setting;
bool is_exclusive;

View File

@ -107,7 +107,7 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
map.compile (plan.map, key);
#ifndef HB_NO_AAT_SHAPE
if (apply_morx)
aat_map.compile (plan.aat_map);
aat_map.compile (plan.aat_map, plan.map);
#endif
#ifndef HB_NO_OT_SHAPE_FRACTIONS
@ -397,12 +397,18 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
map->enable_feature (HB_TAG ('v','e','r','t'), F_GLOBAL_SEARCH);
}
hb_ot_map_feature_flags_t flags = F_NONE;
#ifndef HB_NO_AAT_SHAPE
if (planner->apply_morx)
flags |= F_HAS_FALLBACK; // Allocate bits for all user features so AAT planner can use
#endif
for (unsigned int i = 0; i < num_user_features; i++)
{
const hb_feature_t *feature = &user_features[i];
map->add_feature (feature->tag,
(feature->start == HB_FEATURE_GLOBAL_START &&
feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE,
((feature->start == HB_FEATURE_GLOBAL_START &&
feature->end == HB_FEATURE_GLOBAL_END) ? F_GLOBAL : F_NONE) | flags,
feature->value);
}