Skip to content

Add hb_ot_layout_lookup_collect_glyph_alternates() #5367

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

behdad
Copy link
Member

@behdad behdad commented Jun 15, 2025

No description provided.

behdad added 2 commits June 15, 2025 15:53
To collect all glyph mapping from SingleSubst or AlternateSubst
lookups in one call.  Needed by FreeType autohinter for performance.

New API:
+hb_ot_layout_lookup_collect_glyph_alternates()
* will encode those mappings in a certain encoding:
* If G is the glyph id, and A0, A1, ..., A(n-1) are the alternate glyph ids,
* the mapping will contain the following entries: (G + (i << 24)) -> Ai
* for i = 0, 1, ..., n-1.
Copy link
Contributor

@lemzwerg lemzwerg Jun 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice! However, what I really want is the ability to iterate over all lookup indices, using the same hb_map_t object for each call so that HarfBuzz collects the data cumulatively (i.e., the mapping argument of hb_ot_layout_lookup_collect_glyph_alternates should be of type INOUT).

What about the following scheme: If glyph G has alternates A1, A2, ..., A(n), use the following mapping:

G              ->  N << 24
G + (1 << 24)  ->  A1
G + (2 << 24)  ->  A2
...
G + (N << 24)  ->  A(n)

Now assume three lookup tables X, Y, and Z, with the following data.

X: G -> G1      (SingleSubst)
Y: G -> G2      (SingleSubst)
Z: G -> G3, G4  (AlternateSubst)

After calling hb_ot_layout_lookup_collect_glyph_alternates on X, the mapping would contain

G  ->  G1

after calling the function again on Y, the mapping becomes

G              ->  2 << 24
G + (1 << 24)  ->  G1
G + (2 << 24)  ->  G2

and after calling on Z we have

G              ->  4 << 24
G + (1 << 24)  ->  G1
G + (2 << 24)  ->  G2
G + (3 << 24)  ->  G3
G + (4 << 24)  ->  G4

After processing all lookups I could then iterate over the mapping, ignoring all mapping keys larger than or equal to 1<<24, which I would only look up on demand.

Such a scheme, however, would only fly if both keys and values of the mapping are 64bit integers – it would be tedious otherwise to handle an 'overflow' of alternates. Of course, for current FreeType it would work just fine if we used the lower 16 bits for glyph indices and the upper 16 bits for enumerating alternatives...

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correction: this scheme is better.

G              ->  A1 + (N << 24)
G + (1 << 24)  ->  A2
G + (2 << 24)  ->  A3
...
G + ((N-1) << 24)  ->  A(n)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm going to implement this using two separate maps: one mapping gid to number of alternates. Another, mapping gid to all it's alternates using the proposed encoding of mine. That should address your concerns.

I think I want to stick with 24bit gids and 256 alternates per gid. The extra code to collect more variations of glyphs with more than 256 alternates is about 30 lines of code I think... But also something that I think can completely be ignored.

Copy link
Contributor

@lemzwerg lemzwerg Jun 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, and yes, I agree that the case of having more than 256 alternates could either be ignored (with a warning, say) or implemented in a way that doesn't have to be efficient.

However, it's still not clear to me how I should use your suggested new function. Could you please provide some pseudo-code how iterating over all lookup tables would look like to construct the reverse cmap table?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In pseudocode:

  lookups = hb_set_create();
  for feature_tag in requested_features:
    add feature_tag's lookups to lookups

  glyph_alternates_count = hb_map_create();
  glyph_alternates = hb_map_create();
  for lookup_index in lookups:
    hb_ot_layout_lookup_collect_glyph_alternates(face, lookup_index, glyph_alternates_count, glyph_alternates);

  for gid, count in glyph_alternates_count:
    for (unsigned i = 0; i < count; i++)
    {
      hb_codepoint_t key = gid | (i << 24);
      hb_codepoint_t alternate = glyph_alternates.get(key);

      printf("gid %u alternate %u\n", gid, alternate);
    }

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You combine that with cmap and you get your reverse cmap.

@behdad
Copy link
Member Author

behdad commented Jun 28, 2025

I pushed a new change, which is now causing gcc-14 warnings that I think are spurious. We've seen similar warnings before from gcc. Clang is happy with this code.

behdad:hb 1 (glyphs-alternates*)$ ninja -Cbuild
ninja: Entering directory `build'
[66/277] Compiling C++ object src/libharfbuzz.so.0.61121.0.p/hb-ot-layout.cc.o
In file included from ../src/hb.hh:549,
                 from ../src/hb-ot-layout.cc:31:
In member function ‘hb_pair_t<T1, T2>::operator hb_pair_t<Q1, Q2>() [with Q1 = unsigned int; Q2 = unsigned int; typename hb_enable_if<(std::is_convertible<T1, Q1>::value && std::is_convertible<T2, Q2>::value)>::type* <anonymous> = 0; T1 = unsigned int; T2 = unsigned int&]’,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int, unsigned int&>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int, unsigned int&>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_map_iter_t<hb_zip_iter_t<hb_iota_iter_t<unsigned int, unsigned int>, hb_array_t<const OT::HBGlyphID16> >, OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<unsigned int, unsigned int>)>, hb_function_sortedness_t::NOT_SORTED, 0>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_map_iter_t<hb_zip_iter_t<hb_iota_iter_t<unsigned int, unsigned int>, hb_array_t<const OT::HBGlyphID16> >, OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<unsigned int, unsigned int>)>, hb_function_sortedness_t::NOT_SORTED, 0>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::AlternateSet<Types>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const [with Types = OT::Layout::SmallTypes]’ at ../src/OT/Layout/GSUB/AlternateSet.hh:101:5,
    inlined from ‘OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>)>’ at ../src/OT/Layout/GSUB/AlternateSubstFormat1.hh:80:32,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>)>&; Ts = {hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>)>&; Ts = {hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_zip_iter_t<hb_map_iter_t<hb_array_t<const OT::OffsetTo<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>, OT::NumType<true, short unsigned int>, void, true> >, hb_partial_t<2, const<unnamed struct>*, const OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>*>, hb_function_sortedness_t::NOT_SORTED, 0>, OT::Layout::Common::Coverage::iter_t>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_zip_iter_t<hb_map_iter_t<hb_array_t<const OT::OffsetTo<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>, OT::NumType<true, short unsigned int>, void, true> >, hb_partial_t<2, const<unnamed struct>*, const OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>*>, hb_function_sortedness_t::NOT_SORTED, 0>, OT::Layout::Common::Coverage::iter_t>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::SmallTypes>&, unsigned int>)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::AlternateSubstFormat1_2<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::SmallTypes]’ at ../src/OT/Layout/GSUB/AlternateSubstFormat1.hh:79:5:
../src/hb-algs.hh:766:42: warning: ‘_.hb_pair_t<unsigned int, unsigned int>::second’ may be used uninitialized [-Wmaybe-uninitialized]
  766 |   operator hb_pair_t<Q1, Q2> () { return hb_pair_t<Q1, Q2> (first, second); }
      |                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/hb-algs.hh: In member function ‘void OT::Layout::GSUB_impl::AlternateSubstFormat1_2<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::SmallTypes]’:
../src/hb-algs.hh:517:3: note: ‘_’ declared here
  517 |   impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
      |   ^~~~
In member function ‘hb_pair_t<T1, T2>::operator hb_pair_t<Q1, Q2>() [with Q1 = unsigned int; Q2 = unsigned int; typename hb_enable_if<(std::is_convertible<T1, Q1>::value && std::is_convertible<T2, Q2>::value)>::type* <anonymous> = 0; T1 = unsigned int; T2 = unsigned int&]’,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int, unsigned int&>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int, unsigned int&>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_map_iter_t<hb_zip_iter_t<hb_iota_iter_t<unsigned int, unsigned int>, hb_array_t<const OT::HBGlyphID24> >, OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<unsigned int, unsigned int>)>, hb_function_sortedness_t::NOT_SORTED, 0>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_map_iter_t<hb_zip_iter_t<hb_iota_iter_t<unsigned int, unsigned int>, hb_array_t<const OT::HBGlyphID24> >, OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<unsigned int, unsigned int>)>, hb_function_sortedness_t::NOT_SORTED, 0>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::AlternateSet<Types>::collect_alternates(hb_codepoint_t, hb_map_t*, hb_map_t*) const [with Types = OT::Layout::MediumTypes]’ at ../src/OT/Layout/GSUB/AlternateSet.hh:101:5,
    inlined from ‘OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>)>’ at ../src/OT/Layout/GSUB/AlternateSubstFormat1.hh:80:32,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>)>&; Ts = {hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>)>&; Ts = {hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_zip_iter_t<hb_map_iter_t<hb_array_t<const OT::OffsetTo<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>, OT::NumType<true, unsigned int, 3>, void, true> >, hb_partial_t<2, const<unnamed struct>*, const OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>*>, hb_function_sortedness_t::NOT_SORTED, 0>, OT::Layout::Common::Coverage::iter_t>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_zip_iter_t<hb_map_iter_t<hb_array_t<const OT::OffsetTo<OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>, OT::NumType<true, unsigned int, 3>, void, true> >, hb_partial_t<2, const<unnamed struct>*, const OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>*>, hb_function_sortedness_t::NOT_SORTED, 0>, OT::Layout::Common::Coverage::iter_t>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::AlternateSubstFormat1_2<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_pair_t<const OT::Layout::GSUB_impl::AlternateSet<OT::Layout::MediumTypes>&, unsigned int>)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::AlternateSubstFormat1_2<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::MediumTypes]’ at ../src/OT/Layout/GSUB/AlternateSubstFormat1.hh:79:5:
../src/hb-algs.hh:766:42: warning: ‘_.hb_pair_t<unsigned int, unsigned int>::second’ may be used uninitialized [-Wmaybe-uninitialized]
  766 |   operator hb_pair_t<Q1, Q2> () { return hb_pair_t<Q1, Q2> (first, second); }
      |                                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/hb-algs.hh: In member function ‘void OT::Layout::GSUB_impl::AlternateSubstFormat1_2<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::MediumTypes]’:
../src/hb-algs.hh:517:3: note: ‘_’ declared here
  517 |   impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
      |   ^~~~
In file included from ../src/OT/Layout/GSUB/SingleSubst.hh:5,
                 from ../src/OT/Layout/GSUB/SubstLookupSubTable.hh:5,
                 from ../src/OT/Layout/GSUB/SubstLookup.hh:5,
                 from ../src/OT/Layout/GSUB/GSUB.hh:6,
                 from ../src/hb-ot-layout-gsub-table.hh:32,
                 from ../src/hb-ot-layout.cc:47:
In lambda function,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int&, unsigned int>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int&, unsigned int>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_map_iter_t<OT::Layout::Common::Coverage::iter_t, OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_codepoint_t)>, hb_function_sortedness_t::NOT_SORTED, 0>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_map_iter_t<OT::Layout::Common::Coverage::iter_t, OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_codepoint_t)>, hb_function_sortedness_t::NOT_SORTED, 0>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::SingleSubstFormat1_3<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::SmallTypes]’ at ../src/OT/Layout/GSUB/SingleSubstFormat1.hh:135:5,
    inlined from ‘decltype ((obj.collect_glyph_alternates((forward<Ts>)(hb_collect_glyph_alternates_dispatch_t::_dispatch::ds)...), true)) hb_collect_glyph_alternates_dispatch_t::_dispatch(const T&, hb_priority<1>, Ts&& ...) [with T = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/hb-ot-layout.cc:2665:56,
    inlined from ‘decltype (((hb_collect_glyph_alternates_dispatch_t*)this)->hb_collect_glyph_alternates_dispatch_t::_dispatch(obj, hb_priority<16>(), (forward<Ts>)(hb_collect_glyph_alternates_dispatch_t::dispatch::ds)...)) hb_collect_glyph_alternates_dispatch_t::dispatch(const T&, Ts&& ...) [with T = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::SmallTypes>; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/hb-ot-layout.cc:2672:39,
    inlined from ‘typename context_t::return_t OT::Layout::GSUB_impl::SingleSubst::dispatch(context_t*, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/OT/Layout/GSUB/SingleSubst.hh:33:13,
    inlined from ‘typename context_t::return_t OT::Layout::GSUB_impl::SubstLookupSubTable::dispatch(context_t*, unsigned int, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/OT/Layout/GSUB/SubstLookupSubTable.hh:53:33:
../src/OT/Layout/GSUB/SingleSubstFormat1.hh:136:52: warning: ‘g’ may be used uninitialized [-Wmaybe-uninitialized]
  136 |                 { _hb_collect_glyph_alternates_add (p.first, p.second,
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
  137 |                                                     alternate_count, alternate_glyphs); })
      |                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/hb-algs.hh: In function ‘typename context_t::return_t OT::Layout::GSUB_impl::SubstLookupSubTable::dispatch(context_t*, unsigned int, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’:
../src/hb-algs.hh:517:3: note: ‘g’ was declared here
  517 |   impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
      |   ^~~~
In lambda function,
    inlined from ‘decltype (hb_deref(forward<Appl>(a))((forward<Ts>)(._anon_124::impl::ds)...))<unnamed struct>::impl(Appl&&, hb_priority<0>, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int&, unsigned int>}]’ at ../src/hb-algs.hh:517:53,
    inlined from ‘decltype (((const<unnamed struct>*)this)-><unnamed struct>::impl(forward<Appl>(a), hb_priority<16>(), (forward<Ts>)(._anon_124::operator()::ds)...))<unnamed struct>::operator()(Appl&&, Ts&& ...) const [with Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>&; Ts = {hb_pair_t<unsigned int&, unsigned int>}]’ at ../src/hb-algs.hh:523:44,
    inlined from ‘void hb_apply_t<Appl>::operator()(Iter) [with Iter = hb_map_iter_t<OT::Layout::Common::Coverage::iter_t, OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_codepoint_t)>, hb_function_sortedness_t::NOT_SORTED, 0>; typename hb_enable_if<hb_is_iterator_of<Iter, typename Iter::item_t>::value>::type* <anonymous> = 0; Appl = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)>]’ at ../src/hb-iter.hh:694:7,
    inlined from ‘decltype (forward<Rhs>(rhs)(forward<Lhs>(lhs))) operator|(Lhs&&, Rhs&&) [with Lhs = hb_map_iter_t<OT::Layout::Common::Coverage::iter_t, OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(hb_codepoint_t)>, hb_function_sortedness_t::NOT_SORTED, 0>; Rhs = hb_apply_t<OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const::<lambda(const hb_pair_t<unsigned int, unsigned int>&)> >; typename hb_enable_if<hb_is_iterator_of<Lhs, typename Lhs::item_t>::value>::type* <anonymous> = 0]’ at ../src/hb-iter.hh:369:35,
    inlined from ‘void OT::Layout::GSUB_impl::SingleSubstFormat1_3<Types>::collect_glyph_alternates(hb_map_t*, hb_map_t*) const [with Types = OT::Layout::MediumTypes]’ at ../src/OT/Layout/GSUB/SingleSubstFormat1.hh:135:5,
    inlined from ‘decltype ((obj.collect_glyph_alternates((forward<Ts>)(hb_collect_glyph_alternates_dispatch_t::_dispatch::ds)...), true)) hb_collect_glyph_alternates_dispatch_t::_dispatch(const T&, hb_priority<1>, Ts&& ...) [with T = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/hb-ot-layout.cc:2665:56,
    inlined from ‘decltype (((hb_collect_glyph_alternates_dispatch_t*)this)->hb_collect_glyph_alternates_dispatch_t::_dispatch(obj, hb_priority<16>(), (forward<Ts>)(hb_collect_glyph_alternates_dispatch_t::dispatch::ds)...)) hb_collect_glyph_alternates_dispatch_t::dispatch(const T&, Ts&& ...) [with T = OT::Layout::GSUB_impl::SingleSubstFormat1_3<OT::Layout::MediumTypes>; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/hb-ot-layout.cc:2672:39,
    inlined from ‘typename context_t::return_t OT::Layout::GSUB_impl::SingleSubst::dispatch(context_t*, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/OT/Layout/GSUB/SingleSubst.hh:36:13,
    inlined from ‘typename context_t::return_t OT::Layout::GSUB_impl::SubstLookupSubTable::dispatch(context_t*, unsigned int, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’ at ../src/OT/Layout/GSUB/SubstLookupSubTable.hh:53:33:
../src/OT/Layout/GSUB/SingleSubstFormat1.hh:136:52: warning: ‘g’ may be used uninitialized [-Wmaybe-uninitialized]
  136 |                 { _hb_collect_glyph_alternates_add (p.first, p.second,
      |                   ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~
  137 |                                                     alternate_count, alternate_glyphs); })
      |                                                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
../src/hb-algs.hh: In function ‘typename context_t::return_t OT::Layout::GSUB_impl::SubstLookupSubTable::dispatch(context_t*, unsigned int, Ts&& ...) const [with context_t = hb_collect_glyph_alternates_dispatch_t; Ts = {hb_map_t*&, hb_map_t*&}]’:
../src/hb-algs.hh:517:3: note: ‘g’ was declared here
  517 |   impl (Appl&& a, hb_priority<0>, Ts&&... ds) const HB_AUTO_RETURN
      |   ^~~~

@khaledhosny
Copy link
Collaborator

Do you want to have this for the next release?

@behdad
Copy link
Member Author

behdad commented Jul 25, 2025

I'll see if I can finish it before Monday. But I have notes re VARC and hvgl to write (in a doc)...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants