tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 24e1c732c61e80058b3776c915d55a4426aa5053
parent f467d4a3f61e4e0b9ea0542cfc6c1295ce387af0
Author: Updatebot <updatebot@mozilla.com>
Date:   Sun, 28 Dec 2025 17:55:06 +0000

Bug 2007828 - Update harfbuzz to 12.3.0 r=jfkthame

Differential Revision: https://phabricator.services.mozilla.com/D277567

Diffstat:
Mgfx/harfbuzz/NEWS | 23+++++++++++++++++++++++
Mgfx/harfbuzz/README.md | 61+++++++++++++++++++++++++++++++++++++------------------------
Mgfx/harfbuzz/moz.yaml | 4++--
Mgfx/harfbuzz/src/OT/Color/COLR/COLR.hh | 22+++++++++++-----------
Mgfx/harfbuzz/src/OT/Layout/Common/Coverage.hh | 45++++++++++++++++++++++++++++++---------------
Mgfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh | 34+++++++++++++++++-----------------
Mgfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh | 2+-
Mgfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GPOS/PairPos.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh | 2+-
Mgfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh | 4++--
Mgfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh | 8++++----
Mgfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh | 14+++++++-------
Mgfx/harfbuzz/src/OT/Layout/types.hh | 3+++
Mgfx/harfbuzz/src/check-symbols.py | 8++++----
Mgfx/harfbuzz/src/gen-harfbuzzcc.py | 4++--
Mgfx/harfbuzz/src/graph/classdef-graph.hh | 2+-
Mgfx/harfbuzz/src/graph/coverage-graph.hh | 2+-
Mgfx/harfbuzz/src/graph/graph.hh | 46++++++++++++++++++++++++++++------------------
Mgfx/harfbuzz/src/graph/gsubgpos-context.hh | 1+
Mgfx/harfbuzz/src/graph/gsubgpos-graph.hh | 71+++++++++++++++++++++++++++++++++++++++--------------------------------
Mgfx/harfbuzz/src/graph/ligature-graph.hh | 38++++++++++++++++++++++----------------
Mgfx/harfbuzz/src/graph/markbasepos-graph.hh | 12+++++-------
Mgfx/harfbuzz/src/graph/pairpos-graph.hh | 17+++++++----------
Mgfx/harfbuzz/src/graph/split-helpers.hh | 4++--
Mgfx/harfbuzz/src/hb-aat-layout-common.hh | 84+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
Mgfx/harfbuzz/src/hb-aat-layout-morx-table.hh | 4++--
Mgfx/harfbuzz/src/hb-algs.hh | 20+++++++++++++++++++-
Mgfx/harfbuzz/src/hb-array.hh | 7+++++++
Mgfx/harfbuzz/src/hb-atomic.hh | 6------
Mgfx/harfbuzz/src/hb-cache.hh | 16++++++++++------
Mgfx/harfbuzz/src/hb-common.cc | 37-------------------------------------
Mgfx/harfbuzz/src/hb-config.hh | 4----
Mgfx/harfbuzz/src/hb-debug.hh | 42------------------------------------------
Mgfx/harfbuzz/src/hb-font.hh | 6++++++
Mgfx/harfbuzz/src/hb-kbts.cc | 152+++++++++++++++++++++++++++++--------------------------------------------------
Mgfx/harfbuzz/src/hb-open-file.hh | 14+++++++-------
Mgfx/harfbuzz/src/hb-open-type.hh | 42+++++++++++++++++++++++++++---------------
Mgfx/harfbuzz/src/hb-ot-cmap-table.hh | 39+++++++++++++++------------------------
Mgfx/harfbuzz/src/hb-ot-kern-table.hh | 12++++++------
Mgfx/harfbuzz/src/hb-ot-layout-base-table.hh | 20++++++++++----------
Mgfx/harfbuzz/src/hb-ot-layout-common.hh | 147++++++++++++++++++++++++++++++++-----------------------------------------------
Mgfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh | 173++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
Mgfx/harfbuzz/src/hb-ot-shaper-indic.cc | 48+++---------------------------------------------
Mgfx/harfbuzz/src/hb-ot-shaper-khmer.cc | 6------
Mgfx/harfbuzz/src/hb-ot-stat-table.hh | 20++++++++++----------
Mgfx/harfbuzz/src/hb-ot-var-avar-table.hh | 48++++++++++++++++++++++++++++++++++++++++++++++++
Mgfx/harfbuzz/src/hb-ot-var-common.hh | 87+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mgfx/harfbuzz/src/hb-ot-var-cvar-table.hh | 2+-
Mgfx/harfbuzz/src/hb-outline.cc | 9+++++++++
Mgfx/harfbuzz/src/hb-outline.hh | 1+
Mgfx/harfbuzz/src/hb-sanitize.hh | 82+++++++++----------------------------------------------------------------------
Mgfx/harfbuzz/src/hb-subset-plan-var.cc | 104++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
Mgfx/harfbuzz/src/hb-subset-plan.cc | 3++-
Mgfx/harfbuzz/src/hb-subset-plan.hh | 2+-
Mgfx/harfbuzz/src/hb-vector.hh | 5+++++
Mgfx/harfbuzz/src/hb-version.h | 4++--
Mgfx/harfbuzz/src/rust/Cargo.toml | 2+-
66 files changed, 950 insertions(+), 835 deletions(-)

diff --git a/gfx/harfbuzz/NEWS b/gfx/harfbuzz/NEWS @@ -1,3 +1,26 @@ +Overview of changes leading to 12.3.0 +Saturday, December 27, 2025 +===================================== +- Invalid font tables (eg. GSUB/GPOS) are outright rejected, instead of + partially validated and used. This behavior is different from DirectWrite + and HarfRust, and is in line with CoreText. For context and reasoning see: + https://github.com/harfbuzz/harfbuzz/issues/5535#issuecomment-3573738217 +- Various speed optimizations: + * AAT shaping: speed up state machine on Apple silicon using a fast-path. + 12% faster in LucidaGrande benchmark. + * OpenType shaping: speed up (Chain)Context lookup shaping using a fast-path + and Coverage caching. 20% speedup in NotoNastaliqUrdu benchmark. + * Drawing mega variable-fonts: 30% speedup on GoogleSansFlex benchmark. + * Drawing `VARC` fonts: 5% speedup on varc-hanzi benchmark. +- Always apply synthetic slant around horizontal glyph origin in hb-draw API. +- Fix undefined C++ behavior in some uses union. +- Remove the disabled by default uniscribe-bug-compatible mode from Indic and + Khmer shapers, that used to be used when testing against Uniscribe shaping + behaviour. +- Support full instancing fonts with v2 `avar` table. +- Various subsetting, build, fuzzing, and documentation fixes. + + Overview of changes leading to 12.2.0 Wednesday, November 5, 2025 ===================================== diff --git a/gfx/harfbuzz/README.md b/gfx/harfbuzz/README.md @@ -1,3 +1,9 @@ +# HarfBuzz + +<div align="center"> + +<p><img src="HarfBuzz.png" alt="HarfBuzz Logo" width="256"/></p> + [![Linux CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/linux.yml) [![macoOS CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/macos.yml) [![Windows CI Status](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc.yml/badge.svg)](https://github.com/harfbuzz/harfbuzz/actions/workflows/msvc.yml) @@ -6,36 +12,20 @@ [![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz) - -# HarfBuzz +</div> HarfBuzz is a text shaping engine. It primarily supports [OpenType][1], but also -[Apple Advanced Typography][2]. HarfBuzz is used in Android, Chrome, -ChromeOS, Firefox, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, -PlayStation, Microsoft Edge, Adobe Photoshop, Illustrator, InDesign, -Godot Engine, Unreal Engine, QuarkXPress, Figma, and other places. - -[![xkcd-derived image](xkcd.png)](https://xkcd.com/2347/) - -For bug reports, mailing list, and other information please visit: +[Apple Advanced Typography][2]. +Check “[What is HarfBuzz?](https://harfbuzz.github.io/what-is-harfbuzz.html)” chapter +in the user manual for more inforamation on what HarfBuzz do and what it doesn’t do. - http://harfbuzz.org/ +The canonical source tree and bug trackers are available on [github][4]. +Both development and user support discussion around HarfBuzz happen on +[github][4] as well. For license information, see [COPYING](COPYING). -## Documentation - -For user manual as well as API documentation, check: https://harfbuzz.github.io - -## Download - -For tarball releases of HarfBuzz, look [here][3]. At the same place you -will also find Win32/Win64 binary bundles that include `libharfbuzz` DLL, -`hb-view.exe`, `hb-shape.exe`, and all dependencies. - -The canonical source tree is available on [github][4]. -Both development and user support discussion around HarfBuzz happens on -[github][4] as well. +## API stability The API that comes with `hb.h` will not change incompatibly. Other, peripheral, headers are more likely to go through minor modifications, but again, we do our @@ -49,6 +39,16 @@ As such, we bump the major version number only when we add major new features, the minor version when there is new API, and the micro version when there are bug fixes. +## Documentation + +For user manual as well as API documentation, check: https://harfbuzz.github.io + +## Download + +Tarball releases of HarfBuzz are available on [github releases][3] page. At the same place you +will also find Win32/Win64 binary bundles that include `libharfbuzz` DLL, +`hb-view.exe`, `hb-shape.exe`, and all dependencies. + ## Development For build information, see [BUILD.md](BUILD.md). @@ -108,6 +108,19 @@ transliterated using the Latin script. It also means "talkative" or > TrueType that adds support for complex script rendering, and HarfBuzz is an > implementation of OpenType complex text shaping. +## Users + +HarfBuzz is used in Android, Chrome, +ChromeOS, Firefox, GNOME, GTK+, KDE, Qt, LibreOffice, OpenJDK, XeTeX, Scribus, +PlayStation, Microsoft Edge, Amazon Kindle, Adobe Photoshop, Illustrator, +InDesign, Godot Engine, Unreal Engine, QuarkXPress, Figma, and other places. + +<p align="center"> + <a href="https://xkcd.com/2347/" rel="nofollow"> + <img src="xkcd.png" width="256" alt="xkcd-derived image"> + </a> +</p> + ## Distribution <details> diff --git a/gfx/harfbuzz/moz.yaml b/gfx/harfbuzz/moz.yaml @@ -20,11 +20,11 @@ origin: # Human-readable identifier for this version/release # Generally "version NNN", "tag SSS", "bookmark SSS" - release: 12.2.0 (2025-11-05T07:18:46+02:00). + release: 12.3.0 (2025-12-27T22:33:25+02:00). # Revision to pull in # Must be a long or short commit SHA (long preferred) - revision: 12.2.0 + revision: 12.3.0 # The package's license, where possible using the mnemonic from # https://spdx.org/licenses/ diff --git a/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh b/gfx/harfbuzz/src/OT/Color/COLR/COLR.hh @@ -1613,7 +1613,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c, instancer, VarIdx::NO_VARIATION)); case 2: return_trace (u.format2.subset (c, instancer)); default:return_trace (c->default_return_value ()); @@ -1622,7 +1622,7 @@ struct ClipBox void closurev1 (hb_colrv1_closure_context_t* c) const { - switch (u.format) { + switch (u.format.v) { case 2: u.format2.closurev1 (c); return; default:return; } @@ -1631,9 +1631,9 @@ struct ClipBox template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); default:return_trace (c->default_return_value ()); @@ -1644,7 +1644,7 @@ struct ClipBox const ItemVarStoreInstancer &instancer) const { ClipBoxData clip_box; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_clip_box (clip_box, instancer); break; @@ -1664,7 +1664,7 @@ struct ClipBox protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ ClipBoxFormat1 format1; ClipBoxFormat2 format2; } u; @@ -1844,9 +1844,9 @@ struct Paint template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.paintformat1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.paintformat2, std::forward<Ts> (ds)...)); case 3: return_trace (c->dispatch (u.paintformat3, std::forward<Ts> (ds)...)); @@ -1885,7 +1885,7 @@ struct Paint protected: union { - HBUINT8 format; + struct { HBUINT8 v; } format; PaintColrLayers paintformat1; NoVariable<PaintSolid> paintformat2; Variable<PaintSolid> paintformat3; diff --git a/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh b/gfx/harfbuzz/src/OT/Layout/Common/Coverage.hh @@ -46,7 +46,7 @@ struct Coverage protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CoverageFormat1_3<SmallTypes> format1; CoverageFormat2_4<SmallTypes> format2; #ifndef HB_NO_BEYOND_64K @@ -55,7 +55,7 @@ struct Coverage #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); #ifndef HB_OPTIMIZE_SIZE HB_ALWAYS_INLINE @@ -63,9 +63,9 @@ struct Coverage bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); @@ -86,7 +86,7 @@ struct Coverage unsigned int get (hb_codepoint_t k) const { return get_coverage (k); } unsigned int get_coverage (hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_coverage (glyph_id); case 2: return u.format2.get_coverage (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -111,9 +111,24 @@ struct Coverage return coverage; } + unsigned int get_coverage_binary (hb_codepoint_t glyph_id, + hb_ot_layout_binary_cache_t *cache) const + { + unsigned coverage; + if (cache && cache->get (glyph_id, &coverage)) return coverage < cache->MAX_VALUE ? coverage : NOT_COVERED; + coverage = get_coverage (glyph_id); + if (cache) { + if (coverage == NOT_COVERED) + cache->set_unchecked (glyph_id, cache->MAX_VALUE); + else + cache->set_unchecked (glyph_id, 0); + } + return coverage; + } + unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_population (); case 2: return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -145,11 +160,11 @@ struct Coverage last = g; if (g > max) max = g; } - u.format = !unsorted && count <= num_ranges * 3 ? 1 : 2; + u.format.v = !unsorted && count <= num_ranges * 3 ? 1 : 2; #ifndef HB_NO_BEYOND_64K if (max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (max > 0xFFFFFFu)) #else if (unlikely (max > 0xFFFFu)) @@ -159,7 +174,7 @@ struct Coverage return_trace (false); } - switch (u.format) + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs)); case 2: return_trace (u.format2.serialize (c, glyphs)); @@ -190,7 +205,7 @@ struct Coverage bool intersects (const hb_set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects (glyphs); case 2: return u.format2.intersects (glyphs); @@ -203,7 +218,7 @@ struct Coverage } bool intersects_coverage (const hb_set_t *glyphs, unsigned int index) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersects_coverage (glyphs, index); case 2: return u.format2.intersects_coverage (glyphs, index); @@ -217,7 +232,7 @@ struct Coverage unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -233,7 +248,7 @@ struct Coverage template <typename set_t> bool collect_coverage (set_t *glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.collect_coverage (glyphs); case 2: return u.format2.collect_coverage (glyphs); @@ -249,7 +264,7 @@ struct Coverage hb_requires (hb_is_sink_of (IterableOut, hb_codepoint_t))> void intersect_set (const hb_set_t &glyphs, IterableOut&& intersect_glyphs) const { - switch (u.format) + switch (u.format.v) { case 1: return u.format1.intersect_set (glyphs, intersect_glyphs); case 2: return u.format2.intersect_set (glyphs, intersect_glyphs); @@ -267,7 +282,7 @@ struct Coverage iter_t (const Coverage &c_ = Null (Coverage)) { hb_memset (this, 0, sizeof (*this)); - format = c_.u.format; + format = c_.u.format.v; switch (format) { case 1: u.format1.init (c_.u.format1); return; diff --git a/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh b/gfx/harfbuzz/src/OT/Layout/GDEF/GDEF.hh @@ -252,7 +252,7 @@ struct CaretValue hb_codepoint_t glyph_id, const ItemVariationStore &var_store) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.get_caret_value (font, direction); case 2: return u.format2.get_caret_value (font, direction, glyph_id); case 3: return u.format3.get_caret_value (font, direction, var_store); @@ -263,9 +263,9 @@ struct CaretValue template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -275,7 +275,7 @@ struct CaretValue void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; @@ -289,9 +289,9 @@ struct CaretValue bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -301,13 +301,13 @@ struct CaretValue protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CaretValueFormat1 format1; CaretValueFormat2 format2; CaretValueFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct LigGlyph @@ -519,7 +519,7 @@ struct MarkGlyphSets { bool covers (unsigned int set_index, hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: return u.format1.covers (set_index, glyph_id); default:return false; } @@ -528,7 +528,7 @@ struct MarkGlyphSets template <typename set_t> void collect_coverage (hb_vector_t<set_t> &sets) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_coverage (sets); return; default:return; } @@ -537,7 +537,7 @@ struct MarkGlyphSets void collect_used_mark_sets (const hb_set_t& glyph_set, hb_set_t& used_mark_sets /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: u.format1.collect_used_mark_sets (glyph_set, used_mark_sets); return; default:return; } @@ -546,7 +546,7 @@ struct MarkGlyphSets bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.subset (c)); default:return_trace (false); } @@ -555,9 +555,9 @@ struct MarkGlyphSets bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); default:return_trace (true); } @@ -565,11 +565,11 @@ struct MarkGlyphSets protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkGlyphSetsFormat1 format1; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/Anchor.hh @@ -13,20 +13,20 @@ struct Anchor { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AnchorFormat1 format1; AnchorFormat2 format2; AnchorFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (u.format1.sanitize (c)); case 2: return_trace (u.format2.sanitize (c)); case 3: return_trace (u.format3.sanitize (c)); @@ -38,7 +38,7 @@ struct Anchor float *x, float *y) const { *x = *y = 0; - switch (u.format) { + switch (u.format.v) { case 1: u.format1.get_anchor (c, glyph_id, x, y); return; case 2: u.format2.get_anchor (c, glyph_id, x, y); return; case 3: u.format3.get_anchor (c, glyph_id, x, y); return; @@ -49,7 +49,7 @@ struct Anchor bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: return_trace (bool (reinterpret_cast<Anchor *> (u.format1.copy (c->serializer)))); case 2: if (c->plan->flags & HB_SUBSET_FLAGS_NO_HINTING) @@ -66,7 +66,7 @@ struct Anchor void collect_variation_indices (hb_collect_variation_indices_context_t *c) const { - switch (u.format) { + switch (u.format.v) { case 1: case 2: return; case 3: diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePos.hh @@ -11,7 +11,7 @@ struct CursivePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CursivePosFormat1 format1; } u; @@ -19,9 +19,9 @@ struct CursivePos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); default:return_trace (c->default_return_value ()); } diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/CursivePosFormat1.hh @@ -50,7 +50,7 @@ struct EntryExitRecord DEFINE_SIZE_STATIC (4); }; -static void +static inline void reverse_cursive_minor_offset (hb_glyph_position_t *pos, unsigned int i, hb_direction_t direction, unsigned int new_parent) { int chain = pos[i].attach_chain(), type = pos[i].attach_type(); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkBasePos.hh @@ -11,7 +11,7 @@ struct MarkBasePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkBasePosFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K MarkBasePosFormat1_2<MediumTypes> format2; @@ -22,9 +22,9 @@ struct MarkBasePos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkLigPos.hh @@ -11,7 +11,7 @@ struct MarkLigPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkLigPosFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K MarkLigPosFormat1_2<MediumTypes> format2; @@ -22,9 +22,9 @@ struct MarkLigPos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/MarkMarkPos.hh @@ -11,7 +11,7 @@ struct MarkMarkPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MarkMarkPosFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K MarkMarkPosFormat1_2<MediumTypes> format2; @@ -22,9 +22,9 @@ struct MarkMarkPos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/PairPos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/PairPos.hh @@ -12,7 +12,7 @@ struct PairPos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ PairPosFormat1_3<SmallTypes> format1; PairPosFormat2_4<SmallTypes> format2; #ifndef HB_NO_BEYOND_64K @@ -25,9 +25,9 @@ struct PairPos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh b/gfx/harfbuzz/src/OT/Layout/GPOS/SinglePos.hh @@ -12,7 +12,7 @@ struct SinglePos { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SinglePosFormat1 format1; SinglePosFormat2 format2; } u; @@ -41,7 +41,7 @@ struct SinglePos const hb_hashmap_t<unsigned, hb_pair_t<unsigned, int>> *layout_variation_idx_delta_map, unsigned newFormat) { - if (unlikely (!c->extend_min (u.format))) return; + if (unlikely (!c->extend_min (u.format.v))) return; unsigned format = 2; ValueFormat new_format; new_format = newFormat; @@ -49,8 +49,8 @@ struct SinglePos if (glyph_val_iter_pairs) format = get_format (glyph_val_iter_pairs); - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: u.format1.serialize (c, src, glyph_val_iter_pairs, @@ -70,9 +70,9 @@ struct SinglePos template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); default:return_trace (c->default_return_value ()); diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/AlternateSubst.hh @@ -12,7 +12,7 @@ struct AlternateSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ AlternateSubstFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K AlternateSubstFormat1_2<MediumTypes> format2; @@ -23,9 +23,9 @@ struct AlternateSubst template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); @@ -42,10 +42,10 @@ struct AlternateSubst hb_array_t<const HBGlyphID16> alternate_glyphs_list) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, glyphs, alternate_len_list, alternate_glyphs_list)); default:return_trace (false); } diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSet.hh @@ -105,7 +105,7 @@ struct LigatureSet * * This is replicated in ChainRuleSet and RuleSet. */ - auto &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubst.hh @@ -12,7 +12,7 @@ struct LigatureSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LigatureSubstFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K LigatureSubstFormat1_2<MediumTypes> format2; @@ -23,9 +23,9 @@ struct LigatureSubst template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); @@ -45,10 +45,10 @@ struct LigatureSubst hb_array_t<const HBGlyphID16> component_list /* Starting from second for each ligature */) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, first_glyphs, ligature_per_first_glyph_count_list, diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/LigatureSubstFormat1.hh @@ -107,10 +107,10 @@ struct LigatureSubstFormat1_2 #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE external_cache_t *cache = (external_cache_t *) external_cache; const hb_set_digest_t *seconds = cache ? &cache->seconds : nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr); #else const hb_set_digest_t *seconds = nullptr; - unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); + unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); #endif if (index == NOT_COVERED) return_trace (false); diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/MultipleSubst.hh @@ -12,7 +12,7 @@ struct MultipleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ MultipleSubstFormat1_2<SmallTypes> format1; #ifndef HB_NO_BEYOND_64K MultipleSubstFormat1_2<MediumTypes> format2; @@ -24,9 +24,9 @@ struct MultipleSubst template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); @@ -41,10 +41,10 @@ struct MultipleSubst Iterator it) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned int format = 1; - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, it)); default:return_trace (false); } diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/ReverseChainSingleSubst.hh @@ -12,7 +12,7 @@ struct ReverseChainSingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ReverseChainSingleSubstFormat1 format1; } u; @@ -20,9 +20,9 @@ struct ReverseChainSingleSubst template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); default:return_trace (c->default_return_value ()); } diff --git a/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh b/gfx/harfbuzz/src/OT/Layout/GSUB/SingleSubst.hh @@ -13,7 +13,7 @@ struct SingleSubst { protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ SingleSubstFormat1_3<SmallTypes> format1; SingleSubstFormat2_4<SmallTypes> format2; #ifndef HB_NO_BEYOND_64K @@ -27,9 +27,9 @@ struct SingleSubst template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); #ifndef HB_NO_BEYOND_64K @@ -47,7 +47,7 @@ struct SingleSubst Iterator glyphs) { TRACE_SERIALIZE (this); - if (unlikely (!c->extend_min (u.format))) return_trace (false); + if (unlikely (!c->extend_min (u.format.v))) return_trace (false); unsigned format = 2; unsigned delta = 0; if (glyphs) @@ -71,8 +71,8 @@ struct SingleSubst if (!hb_all (++(+glyphs), delta, get_delta)) format += 1; } - u.format = format; - switch (u.format) { + u.format.v = format; + switch (u.format.v) { case 1: return_trace (u.format1.serialize (c, + glyphs | hb_map_retains_sorting (hb_first), diff --git a/gfx/harfbuzz/src/OT/Layout/types.hh b/gfx/harfbuzz/src/OT/Layout/types.hh @@ -32,6 +32,9 @@ using hb_ot_layout_mapping_cache_t = hb_cache_t<15, 8, 7>; static_assert (sizeof (hb_ot_layout_mapping_cache_t) == 256, ""); +using hb_ot_layout_binary_cache_t = hb_cache_t<14, 1, 7>; +static_assert (sizeof (hb_ot_layout_binary_cache_t) == 128, ""); + namespace OT { namespace Layout { diff --git a/gfx/harfbuzz/src/check-symbols.py b/gfx/harfbuzz/src/check-symbols.py @@ -38,10 +38,10 @@ IGNORED_SYMBOLS = [ # Rust IGNORED_SYMBOLS += [ "rust_eh_personality", - "_ZN3std9panicking11EMPTY_PANIC.*", # 'std::panicking::EMPTY_PANIC::.*' - "_ZN3std3sys3pal4unix4args3imp15ARGV_INIT_ARRAY.*", # 'std::sys::pal::unix::args::imp::ARGV_INIT_ARRAY::.*' - "_ZN3std3sys4args4unix3imp15ARGV_INIT_ARRAY.*", # std::sys::args::unix::imp::ARGV_INIT_ARRAY::.* - "_ZN17compiler_builtins.*", # 'compiler_builtins::.*' + "_.*3std9panicking11EMPTY_PANIC.*", # 'std.*::panicking::EMPTY_PANIC::.*' + "_.*3std3sys3pal4unix4args3imp15ARGV_INIT_ARRAY.*", # 'std.*::sys::pal::unix::args::imp::ARGV_INIT_ARRAY::.*' + "_.*3std3sys4args4unix3imp15ARGV_INIT_ARRAY.*", # std.*::sys::args::unix::imp::ARGV_INIT_ARRAY::.* + "_.*17compiler_builtins.*", # 'compiler_builtins.*::.*' ".*__rustc.*", # '.*__rustc.*' # Eg. _RNvCsgSLETaxrkfn_7___rustc17___rust_probestack '_hb_harfrust_.*_rs', ] diff --git a/gfx/harfbuzz/src/gen-harfbuzzcc.py b/gfx/harfbuzz/src/gen-harfbuzzcc.py @@ -2,7 +2,7 @@ "This tool is intended to be used from meson" -import os, sys, shutil +import os, sys, shutil, pathlib if len (sys.argv) < 3: sys.exit (__doc__) @@ -14,7 +14,7 @@ CURRENT_SOURCE_DIR = sys.argv[2] sources = sorted(set(sys.argv[3:])) with open (OUTPUT, "wb") as f: - f.write ("".join ('#include "{}"\n'.format (os.path.relpath (os.path.abspath (x), CURRENT_SOURCE_DIR)) for x in sources if x.endswith (".cc")).encode ()) + f.write ("".join ('#include "{}"\n'.format (pathlib.Path( os.path.relpath (os.path.abspath (x), CURRENT_SOURCE_DIR) ).as_posix()) for x in sources if x.endswith (".cc")).encode ()) # copy it also to the source tree, but only if it has changed baseline_filename = os.path.join (CURRENT_SOURCE_DIR, os.path.basename (OUTPUT)) diff --git a/gfx/harfbuzz/src/graph/classdef-graph.hh b/gfx/harfbuzz/src/graph/classdef-graph.hh @@ -117,7 +117,7 @@ struct ClassDef : public OT::ClassDef int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::ClassDef::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return ((ClassDefFormat1*)this)->sanitize (vertex); case 2: return ((ClassDefFormat2*)this)->sanitize (vertex); diff --git a/gfx/harfbuzz/src/graph/coverage-graph.hh b/gfx/harfbuzz/src/graph/coverage-graph.hh @@ -161,7 +161,7 @@ struct Coverage : public OT::Layout::Common::Coverage int64_t vertex_len = vertex.obj.tail - vertex.obj.head; if (vertex_len < OT::Layout::Common::Coverage::min_size) return false; hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: return graph::sanitize ((const OT::Layout::Common::CoverageFormat1_3<OT::Layout::SmallTypes>*) this, vertex); case 2: return graph::sanitize ((const OT::Layout::Common::CoverageFormat2_4<OT::Layout::SmallTypes>*) this, vertex); diff --git a/gfx/harfbuzz/src/graph/graph.hh b/gfx/harfbuzz/src/graph/graph.hh @@ -1104,24 +1104,6 @@ struct graph_t * * Returns the index of the newly created duplicate. * - * If the child_idx only has incoming edges from parent_idx, this - * will do nothing and return the original child_idx. - */ - unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) - { - unsigned new_idx = duplicate (parent_idx, child_idx); - if (new_idx == (unsigned) -1) return child_idx; - return new_idx; - } - - - /* - * Creates a copy of child and re-assigns the link from - * parent to the clone. The copy is a shallow copy, objects - * linked from child are not duplicated. - * - * Returns the index of the newly created duplicate. - * * If the child_idx only has incoming edges from parent_idx, * duplication isn't possible and this will return -1. */ @@ -1259,6 +1241,34 @@ struct graph_t } /* + * Creates a new child node and remap the old child to it. + * + * Returns the index of the newly created child. + * + */ + unsigned remap_child (unsigned parent_idx, unsigned old_child_idx) + { + unsigned new_child_idx = duplicate (old_child_idx); + if (new_child_idx == (unsigned) -1) return -1; + + auto& parent = vertices_[parent_idx]; + for (auto& l : parent.obj.real_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, false); + } + + for (auto& l : parent.obj.virtual_links) + { + if (l.objidx != old_child_idx) + continue; + reassign_link (l, parent_idx, new_child_idx, true); + } + return new_child_idx; + } + + /* * Raises the sorting priority of all children. */ bool raise_childrens_priority (unsigned parent_idx) diff --git a/gfx/harfbuzz/src/graph/gsubgpos-context.hh b/gfx/harfbuzz/src/graph/gsubgpos-context.hh @@ -41,6 +41,7 @@ struct gsubgpos_graph_context_t unsigned lookup_list_index; hb_hashmap_t<unsigned, graph::Lookup*> lookups; hb_hashmap_t<unsigned, unsigned> subtable_to_extension; + hb_hashmap_t<unsigned, hb_vector_t<unsigned>> split_subtables; HB_INTERNAL gsubgpos_graph_context_t (hb_tag_t table_tag_, graph_t& graph_); diff --git a/gfx/harfbuzz/src/graph/gsubgpos-graph.hh b/gfx/harfbuzz/src/graph/gsubgpos-graph.hh @@ -138,10 +138,8 @@ struct Lookup : public OT::Lookup for (unsigned i = 0; i < subTable.len; i++) { unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]); - unsigned parent_index = this_index; if (is_ext) { unsigned ext_subtable_index = subtable_index; - parent_index = ext_subtable_index; ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension = (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) c.graph.object (ext_subtable_index).head; @@ -154,33 +152,43 @@ struct Lookup : public OT::Lookup continue; } - hb_vector_t<unsigned> new_sub_tables; - - if (c.table_tag == HB_OT_TAG_GPOS) { - switch (type) - { - case 2: - new_sub_tables = split_subtable<PairPos> (c, parent_index, subtable_index); break; - case 4: - new_sub_tables = split_subtable<MarkBasePos> (c, parent_index, subtable_index); break; - default: - break; - } - } else if (c.table_tag == HB_OT_TAG_GSUB) { - switch (type) - { - case 4: - new_sub_tables = split_subtable<graph::LigatureSubst> (c, parent_index, subtable_index); break; - default: - break; - } + hb_vector_t<unsigned>* split_result; + if (c.split_subtables.has (subtable_index, &split_result)) + { + if (split_result->length == 0) + continue; + all_new_subtables.push (hb_pair(i, *split_result)); } + else + { + hb_vector_t<unsigned> new_sub_tables; + + if (c.table_tag == HB_OT_TAG_GPOS) { + switch (type) + { + case 2: + new_sub_tables = split_subtable<PairPos> (c, subtable_index); break; + case 4: + new_sub_tables = split_subtable<MarkBasePos> (c, subtable_index); break; + default: + break; + } + } else if (c.table_tag == HB_OT_TAG_GSUB) { + switch (type) + { + case 4: + new_sub_tables = split_subtable<graph::LigatureSubst> (c, subtable_index); break; + default: + break; + } + } - if (new_sub_tables.in_error ()) return false; - if (!new_sub_tables) continue; - hb_pair_t<unsigned, hb_vector_t<unsigned>>* entry = all_new_subtables.push (); - entry->first = i; - entry->second = std::move (new_sub_tables); + if (new_sub_tables.in_error ()) return false; + + c.split_subtables.set (subtable_index, new_sub_tables); + if (new_sub_tables) + all_new_subtables.push (hb_pair (i, std::move (new_sub_tables))); + } } if (all_new_subtables) { @@ -192,20 +200,19 @@ struct Lookup : public OT::Lookup template<typename T> hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c, - unsigned parent_idx, unsigned objidx) { T* sub_table = (T*) c.graph.object (objidx).head; if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx])) return hb_vector_t<unsigned> (); - return sub_table->split_subtables (c, parent_idx, objidx); + return sub_table->split_subtables (c, objidx); } bool add_sub_tables (gsubgpos_graph_context_t& c, unsigned this_index, unsigned type, - hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) + const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) { bool is_ext = is_extension (c.table_tag); auto* v = &c.graph.vertices_[this_index]; @@ -272,7 +279,7 @@ struct Lookup : public OT::Lookup void fix_existing_subtable_links (gsubgpos_graph_context_t& c, unsigned this_index, - hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) + const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids) { auto& v = c.graph.vertices_[this_index]; Lookup* lookup = (Lookup*) v.obj.head; @@ -326,7 +333,7 @@ struct Lookup : public OT::Lookup unsigned* existing_ext_index = nullptr; if (c.subtable_to_extension.has(subtable_index, &existing_ext_index)) { ext_index = *existing_ext_index; - } else { + } else { ext_index = create_extension_subtable(c, subtable_index, type); c.subtable_to_extension.set(subtable_index, ext_index); } diff --git a/gfx/harfbuzz/src/graph/ligature-graph.hh b/gfx/harfbuzz/src/graph/ligature-graph.hh @@ -67,14 +67,13 @@ struct LigatureSubstFormat1 : public OT::Layout::GSUB_impl::LigatureSubstFormat1 } hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - auto split_points = compute_split_points(c, parent_index, this_index); + auto split_points = compute_split_points(c, this_index); split_context_t split_context { c, this, - c.graph.duplicate_if_shared (parent_index, this_index), + this_index, total_number_ligas(c, this_index), liga_counts(c, this_index), }; @@ -123,7 +122,6 @@ struct LigatureSubstFormat1 : public OT::Layout::GSUB_impl::LigatureSubstFormat1 } hb_vector_t<unsigned> compute_split_points(gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) const { // For ligature subst coverage is always packed last, and as a result is where an overflow @@ -441,26 +439,35 @@ struct LigatureSubstFormat1 : public OT::Layout::GSUB_impl::LigatureSubstFormat1 } // Adjust liga set array - c.graph.vertices_[this_index].obj.tail -= (ligatureSet.len - new_liga_set_count) * SmallTypes::size; + auto& this_vertex = c.graph.vertices_[this_index]; + this_vertex.obj.tail -= (ligatureSet.len - new_liga_set_count) * SmallTypes::size; ligatureSet.len = new_liga_set_count; // Coverage matches the number of liga sets so rebuild as needed - auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage); - if (!coverage) return false; + unsigned coverage_idx = c.graph.index_for_offset (this_index, &this->coverage); + if (coverage_idx == (unsigned) -1) return false; + + auto& coverage_v = c.graph.vertices_[coverage_idx]; + if (coverage_v.is_shared ()) + { + coverage_idx = c.graph.remap_child (this_index, coverage_idx); + if (coverage_idx == (unsigned) -1) return false; + } for (unsigned i : retained_indices.iter()) - add_virtual_link(c, i, coverage.index); + add_virtual_link(c, i, coverage_idx); - unsigned coverage_size = coverage.vertex->table_size (); + unsigned coverage_size = coverage_v.table_size (); + Coverage* coverage_table = (Coverage*) coverage_v.obj.head; auto new_coverage = - + hb_zip (coverage.table->iter (), hb_range ()) + + hb_zip (coverage_table->iter (), hb_range ()) | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) { return p.second < new_liga_set_count; }) | hb_map_retains_sorting (hb_first) ; - return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); + return Coverage::make_coverage (c, new_coverage, coverage_idx, coverage_size); } }; @@ -468,12 +475,11 @@ struct LigatureSubst : public OT::Layout::GSUB_impl::LigatureSubst { hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((LigatureSubstFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((LigatureSubstFormat1*)(&u.format1))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; // Don't split 24bit Ligature Subs @@ -486,10 +492,10 @@ struct LigatureSubst : public OT::Layout::GSUB_impl::LigatureSubst bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - if (vertex_len < u.format.get_size ()) return false; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((LigatureSubstFormat1*)(&u.format1))->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/graph/markbasepos-graph.hh b/gfx/harfbuzz/src/graph/markbasepos-graph.hh @@ -212,7 +212,6 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S } hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -265,7 +264,7 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S split_context_t split_context { c, this, - c.graph.duplicate_if_shared (parent_index, this_index), + this_index, std::move (class_to_info), c.graph.vertices_[mark_array_id].position_to_index_map (), }; @@ -478,12 +477,11 @@ struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<S struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos { hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 2: HB_FALLTHROUGH; // Don't split 24bit MarkBasePos's. @@ -496,10 +494,10 @@ struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - if (vertex_len < u.format.get_size ()) return false; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex); #ifndef HB_NO_BEYOND_64K diff --git a/gfx/harfbuzz/src/graph/pairpos-graph.hh b/gfx/harfbuzz/src/graph/pairpos-graph.hh @@ -49,7 +49,6 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType } hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { hb_set_t visited; @@ -84,7 +83,7 @@ struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallType split_context_t split_context { c, this, - c.graph.duplicate_if_shared (parent_index, this_index), + this_index, }; return actuate_subtable_split<split_context_t> (split_context, split_points); @@ -207,7 +206,6 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType } hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; @@ -291,7 +289,7 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType split_context_t split_context { c, this, - c.graph.duplicate_if_shared (parent_index, this_index), + this_index, class1_record_size, total_value_len, value_1_len, @@ -607,14 +605,13 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallType struct PairPos : public OT::Layout::GPOS_impl::PairPos { hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, - unsigned parent_index, unsigned this_index) { - switch (u.format) { + switch (u.format.v) { case 1: - return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index); case 2: - return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); + return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index); #ifndef HB_NO_BEYOND_64K case 3: HB_FALLTHROUGH; case 4: HB_FALLTHROUGH; @@ -628,10 +625,10 @@ struct PairPos : public OT::Layout::GPOS_impl::PairPos bool sanitize (graph_t::vertex_t& vertex) const { int64_t vertex_len = vertex.obj.tail - vertex.obj.head; - if (vertex_len < u.format.get_size ()) return false; + if (vertex_len < u.format.v.get_size ()) return false; hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); case 2: diff --git a/gfx/harfbuzz/src/graph/split-helpers.hh b/gfx/harfbuzz/src/graph/split-helpers.hh @@ -49,7 +49,7 @@ hb_vector_t<unsigned> actuate_subtable_split (Context& split_context, if (id == (unsigned) -1) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); return new_objects; } new_objects.push (id); @@ -58,7 +58,7 @@ hb_vector_t<unsigned> actuate_subtable_split (Context& split_context, if (!split_context.shrink (split_points[0])) { new_objects.reset (); - new_objects.allocated = -1; // mark error + new_objects.ensure_error (); } return new_objects; diff --git a/gfx/harfbuzz/src/hb-aat-layout-common.hh b/gfx/harfbuzz/src/hb-aat-layout-common.hh @@ -692,7 +692,7 @@ struct Lookup { const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_value (glyph_id, num_glyphs); case 2: hb_barrier (); return u.format2.get_value (glyph_id); case 4: hb_barrier (); return u.format4.get_value (glyph_id); @@ -704,7 +704,7 @@ struct Lookup const typename T::type get_value_or_null (hb_codepoint_t glyph_id, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { /* Format 10 cannot return a pointer. */ case 10: hb_barrier (); return u.format10.get_value_or_null (glyph_id); default: @@ -716,7 +716,7 @@ struct Lookup template <typename set_t> void collect_glyphs (set_t &glyphs, unsigned int num_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs (glyphs, num_glyphs); return; case 2: hb_barrier (); u.format2.collect_glyphs (glyphs); return; case 4: hb_barrier (); u.format4.collect_glyphs (glyphs); return; @@ -729,7 +729,7 @@ struct Lookup template <typename set_t, typename filter_t> void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0.collect_glyphs_filtered (glyphs, num_glyphs, filter); return; case 2: hb_barrier (); u.format2.collect_glyphs_filtered (glyphs, filter); return; case 4: hb_barrier (); u.format4.collect_glyphs_filtered (glyphs, filter); return; @@ -751,9 +751,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c)); @@ -766,9 +766,9 @@ struct Lookup bool sanitize (hb_sanitize_context_t *c, const void *base) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c, base)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c, base)); case 4: hb_barrier (); return_trace (u.format4.sanitize (c, base)); @@ -781,7 +781,7 @@ struct Lookup protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ LookupFormat0<T> format0; LookupFormat2<T> format2; LookupFormat4<T> format4; @@ -790,7 +790,7 @@ struct Lookup LookupFormat10<T> format10; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2); @@ -1216,11 +1216,24 @@ struct StateTableDriver int state = StateTableT::STATE_START_OF_TEXT; // If there's only one range, we already checked the flag. auto *last_range = ac->range_flags && (ac->range_flags->length > 1) ? &(*ac->range_flags)[0] : nullptr; + const bool start_state_safe_to_break_eot = + !c->table->is_actionable (machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_END_OF_TEXT)); for (buffer->idx = 0; buffer->successful;) { - /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ - if (last_range) + unsigned int klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + resume: + 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); + + bool is_not_epsilon_transition = !(entry.flags & Flags::DontAdvance); + bool is_not_actionable = !c->table->is_actionable (entry); + + if (unlikely (last_range)) { + /* This block is copied in NoncontextualSubtable::apply. Keep in sync. */ auto *range = last_range; if (buffer->idx < buffer->len) { @@ -1235,7 +1248,7 @@ struct StateTableDriver } if (!(range->flags & ac->subtable_flags)) { - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; state = StateTableT::STATE_START_OF_TEXT; @@ -1243,13 +1256,42 @@ struct StateTableDriver continue; } } + else + { + // Fast path for when transitioning from start-state to start-state with + // no action and advancing. Do so as long as the class remains the same. + // This is common with runs of non-actionable glyphs. + + bool is_null_transition = state == StateTableT::STATE_START_OF_TEXT && + next_state == StateTableT::STATE_START_OF_TEXT && + start_state_safe_to_break_eot && + is_not_actionable && + is_not_epsilon_transition && + !last_range; + + if (is_null_transition) + { + unsigned old_klass = klass; + do + { + c->transition (buffer, this, entry); - unsigned int klass = likely (buffer->idx < buffer->len) ? - machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : - (unsigned) 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); + if (buffer->idx == buffer->len || !buffer->successful) + break; + + (void) buffer->next_glyph (); + + klass = likely (buffer->idx < buffer->len) ? + machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) : + (unsigned) CLASS_END_OF_TEXT; + } while (klass == old_klass); + + if (buffer->idx == buffer->len || !buffer->successful) + break; + + goto resume; + } + } /* Conditions under which it's guaranteed safe-to-break before current glyph: * @@ -1316,10 +1358,10 @@ struct StateTableDriver state = next_state; DEBUG_MSG (APPLY, nullptr, "s%d", state); - if (buffer->idx == buffer->len || unlikely (!buffer->successful)) + if (buffer->idx == buffer->len) break; - if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0) + if (is_not_epsilon_transition || buffer->max_ops-- <= 0) (void) buffer->next_glyph (); } diff --git a/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh b/gfx/harfbuzz/src/hb-aat-layout-morx-table.hh @@ -487,7 +487,7 @@ struct LigatureSubtable if (entry.flags & LigatureEntryT::SetComponent) { /* Never mark same index twice, in case DontAdvance was used... */ - if (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len) + if (unlikely (match_length && match_positions[(match_length - 1u) % ARRAY_LENGTH (match_positions)] == buffer->out_len)) match_length--; match_positions[match_length++ % ARRAY_LENGTH (match_positions)] = buffer->out_len; @@ -640,7 +640,7 @@ struct NoncontextualSubtable for (unsigned int i = 0; i < count; i++) { /* This block copied from StateTableDriver::drive. Keep in sync. */ - if (last_range) + if (unlikely (last_range)) { auto *range = last_range; { diff --git a/gfx/harfbuzz/src/hb-algs.hh b/gfx/harfbuzz/src/hb-algs.hh @@ -98,7 +98,8 @@ struct __attribute__((packed)) hb_packed_t { Type v; }; (__BYTE_ORDER == __BIG_ENDIAN || \ (__BYTE_ORDER == __LITTLE_ENDIAN && \ hb_has_builtin(__builtin_bswap16) && \ - hb_has_builtin(__builtin_bswap32))) + hb_has_builtin(__builtin_bswap32) && \ + hb_has_builtin(__builtin_bswap64))) #define HB_FAST_NUM_ACCESS 1 #else #define HB_FAST_NUM_ACCESS 0 @@ -222,6 +223,14 @@ struct HBInt<BE, Type, 8> HBInt () = default; HBInt (Type V) +#if HB_FAST_NUM_ACCESS + { + if (BE == (__BYTE_ORDER == __BIG_ENDIAN)) + ((hb_packed_t<uint64_t> *) v)->v = V; + else + ((hb_packed_t<uint64_t> *) v)->v = __builtin_bswap64 (V); + } +#else : v {BE ? uint8_t ((V >> 56) & 0xFF) : uint8_t ((V ) & 0xFF), BE ? uint8_t ((V >> 48) & 0xFF) : uint8_t ((V >> 8) & 0xFF), BE ? uint8_t ((V >> 40) & 0xFF) : uint8_t ((V >> 16) & 0xFF), @@ -230,8 +239,16 @@ struct HBInt<BE, Type, 8> BE ? uint8_t ((V >> 16) & 0xFF) : uint8_t ((V >> 40) & 0xFF), BE ? uint8_t ((V >> 8) & 0xFF) : uint8_t ((V >> 48) & 0xFF), BE ? uint8_t ((V ) & 0xFF) : uint8_t ((V >> 56) & 0xFF)} {} +#endif constexpr operator Type () const { +#if HB_FAST_NUM_ACCESS + return (BE == (__BYTE_ORDER == __BIG_ENDIAN)) ? + ((const hb_packed_t<uint64_t> *) v)->v + : + __builtin_bswap64 (((const hb_packed_t<uint64_t> *) v)->v) + ; +#else return (BE ? (uint64_t (v[0]) << 56) : (uint64_t (v[0]) )) + (BE ? (uint64_t (v[1]) << 48) : (uint64_t (v[1]) << 8)) + (BE ? (uint64_t (v[2]) << 40) : (uint64_t (v[2]) << 16)) @@ -240,6 +257,7 @@ struct HBInt<BE, Type, 8> + (BE ? (uint64_t (v[5]) << 16) : (uint64_t (v[5]) << 40)) + (BE ? (uint64_t (v[6]) << 8) : (uint64_t (v[6]) << 48)) + (BE ? (uint64_t (v[7]) ) : (uint64_t (v[7]) << 56)); +#endif } private: uint8_t v[8]; }; diff --git a/gfx/harfbuzz/src/hb-array.hh b/gfx/harfbuzz/src/hb-array.hh @@ -291,6 +291,13 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&> && (unsigned int) (arrayZ + length - (const char *) p) >= size; } + template <unsigned P = sizeof (Type), + hb_enable_if (P == 1)> + bool check_end (const void *p) const + { + return (uintptr_t) (((const char *) p) - arrayZ) <= length; + } + /* Only call if you allocated the underlying array using hb_malloc() or similar. */ void fini () { hb_free ((void *) arrayZ); arrayZ = nullptr; length = 0; } diff --git a/gfx/harfbuzz/src/hb-atomic.hh b/gfx/harfbuzz/src/hb-atomic.hh @@ -169,11 +169,6 @@ struct hb_atomic_t int operator++ (int) { return inc (); } int operator-- (int) { return dec (); } - long operator|= (long v_) - { - set_relaxed (get_relaxed () | v_); - return *this; - } friend void swap (hb_atomic_t &a, hb_atomic_t &b) noexcept { @@ -236,7 +231,6 @@ struct hb_atomic_t int operator ++ (int) { return inc (); } int operator -- (int) { return dec (); } - long operator |= (long v_) { set_relaxed (get_relaxed () | v_); return *this; } T v = 0; }; diff --git a/gfx/harfbuzz/src/hb-cache.hh b/gfx/harfbuzz/src/hb-cache.hh @@ -64,12 +64,16 @@ template <unsigned int key_bits=16, struct hb_cache_t { using item_t = typename std::conditional<thread_safe, - typename std::conditional<key_bits + value_bits - cache_bits <= 16, - hb_atomic_t<unsigned short>, - hb_atomic_t<unsigned int>>::type, - typename std::conditional<key_bits + value_bits - cache_bits <= 16, - unsigned short, - unsigned int>::type + typename std::conditional<key_bits + value_bits - cache_bits <= 8, + hb_atomic_t<unsigned char>, + typename std::conditional<key_bits + value_bits - cache_bits <= 16, + hb_atomic_t<unsigned short>, + hb_atomic_t<unsigned int>>::type>::type, + typename std::conditional<key_bits + value_bits - cache_bits <= 8, + unsigned char, + typename std::conditional<key_bits + value_bits - cache_bits <= 16, + unsigned short, + unsigned int>::type>::type >::type; static_assert ((key_bits >= cache_bits), ""); diff --git a/gfx/harfbuzz/src/hb-common.cc b/gfx/harfbuzz/src/hb-common.cc @@ -40,43 +40,6 @@ **/ -/* hb_options_t */ - -hb_atomic_t<unsigned> _hb_options; - -void -_hb_options_init () -{ - hb_options_union_t u; - u.i = 0; - u.opts.initialized = true; - - const char *c = getenv ("HB_OPTIONS"); - if (c) - { - while (*c) - { - const char *p = strchr (c, ':'); - if (!p) - p = c + strlen (c); - -#define OPTION(name, symbol) \ - if (0 == strncmp (c, name, p - c) && strlen (name) == static_cast<size_t>(p - c)) do { u.opts.symbol = true; } while (0) - - OPTION ("uniscribe-bug-compatible", uniscribe_bug_compatible); - -#undef OPTION - - c = *p ? p + 1 : p; - } - - } - - /* This is idempotent and threadsafe. */ - _hb_options = u.i; -} - - /* hb_tag_t */ /** diff --git a/gfx/harfbuzz/src/hb-config.hh b/gfx/harfbuzz/src/hb-config.hh @@ -145,10 +145,6 @@ #define HB_NO_PAINT #endif -#ifdef HB_NO_GETENV -#define HB_NO_UNISCRIBE_BUG_COMPATIBLE -#endif - #ifdef HB_NO_LEGACY #define HB_NO_CMAP_LEGACY_SUBTABLES #define HB_NO_FALLBACK_SHAPE diff --git a/gfx/harfbuzz/src/hb-debug.hh b/gfx/harfbuzz/src/hb-debug.hh @@ -38,48 +38,6 @@ /* - * Global runtime options. - */ - -struct hb_options_t -{ - bool unused : 1; /* In-case sign bit is here. */ - bool initialized : 1; - bool uniscribe_bug_compatible : 1; -}; - -union hb_options_union_t { - unsigned i; - hb_options_t opts; -}; -static_assert ((sizeof (hb_atomic_t<unsigned>) >= sizeof (hb_options_union_t)), ""); - -HB_INTERNAL void -_hb_options_init (); - -extern HB_INTERNAL hb_atomic_t<unsigned> _hb_options; - -static inline hb_options_t -hb_options () -{ -#ifdef HB_NO_GETENV - return hb_options_t (); -#endif - /* Make a local copy, so we can access bitfield threadsafely. */ - hb_options_union_t u; - u.i = _hb_options; - - if (unlikely (!u.i)) - { - _hb_options_init (); - u.i = _hb_options; - } - - return u.opts; -} - - -/* * Debug output (needs enabling at compile time.) */ diff --git a/gfx/harfbuzz/src/hb-font.hh b/gfx/harfbuzz/src/hb-font.hh @@ -719,7 +719,13 @@ struct hb_font_t // Slant before embolden; produces nicer results. if (slanted) + { + hb_position_t xo = 0, yo = 0; + get_glyph_h_origin (glyph, &xo, &yo, false); + outline.translate (-xo, -yo); outline.slant (slant_xy); + outline.translate (xo, yo); + } if (embolden) { diff --git a/gfx/harfbuzz/src/hb-kbts.cc b/gfx/harfbuzz/src/hb-kbts.cc @@ -24,13 +24,16 @@ #include "hb.hh" -#if HAVE_KBTS +#ifdef HAVE_KBTS #include "hb-shaper-impl.hh" #define KB_TEXT_SHAPE_IMPLEMENTATION #define KB_TEXT_SHAPE_STATIC #define KB_TEXT_SHAPE_NO_CRT +#define KBTS_MALLOC(a, b) hb_malloc(b) +#define KBTS_FREE(a, b) hb_free(b) +#define KBTS_MEMCPY hb_memcpy #define KBTS_MEMSET hb_memset #pragma GCC diagnostic push @@ -55,40 +58,21 @@ _hb_kbts_shaper_face_data_create (hb_face_t *face) return nullptr; } - void *data = hb_malloc (blob_length); - if (likely (data)) - hb_memcpy (data, blob_data, blob_length); - - hb_blob_destroy (blob); - blob = nullptr; - - if (unlikely (!data)) - { - DEBUG_MSG (KBTS, face, "Failed to allocate memory for font data"); - return nullptr; - } - kbts_font *kb_font = (kbts_font *) hb_calloc (1, sizeof (kbts_font)); if (unlikely (!kb_font)) { - hb_free (data); + hb_blob_destroy (blob); return nullptr; } - size_t memory_size; - { - unsigned scratch_size = kbts_ReadFontHeader (kb_font, data, blob_length); - void *scratch = hb_malloc (scratch_size); - memory_size = kbts_ReadFontData (kb_font, scratch, scratch_size); - hb_free (scratch); - } + *kb_font = kbts_FontFromMemory((void *)blob_data, blob_length, face->index, nullptr, nullptr); + hb_blob_destroy (blob); + blob = nullptr; - void *memory = hb_malloc (memory_size); - if (unlikely (!kbts_PostReadFontInitialize (kb_font, memory, memory_size))) + if (unlikely (!kbts_FontIsValid (kb_font))) { - DEBUG_MSG (KBTS, face, "kbts_PostReadFontInitialize failed"); - hb_free (memory); - hb_free (data); + DEBUG_MSG (KBTS, face, "Failed create font from data"); + kbts_FreeFont (kb_font); hb_free (kb_font); return nullptr; } @@ -103,8 +87,7 @@ _hb_kbts_shaper_face_data_destroy (hb_kbts_face_data_t *data) assert (kbts_FontIsValid (font)); - hb_free (font->FileBase); - hb_free (font->GlyphLookupMatrix); + kbts_FreeFont (font); hb_free (font); } @@ -144,6 +127,10 @@ _hb_kbts_shape (hb_shape_plan_t *shape_plan, return false; } + kbts_glyph_storage kb_glyph_storage; + if (unlikely (!kbts_InitializeGlyphStorage (&kb_glyph_storage, nullptr, nullptr))) + return false; + kbts_script kb_script = KBTS_SCRIPT_DONT_KNOW; kbts_language kb_language = KBTS_LANGUAGE_DEFAULT; { @@ -167,93 +154,66 @@ _hb_kbts_shape (hb_shape_plan_t *shape_plan, kb_language = (kbts_language) hb_uint32_swap (language); } - hb_vector_t<kbts_glyph> kb_glyphs; - if (unlikely (!kb_glyphs.resize_full (buffer->len, false, true))) - return false; - for (size_t i = 0; i < buffer->len; ++i) - kb_glyphs.arrayZ[i] = kbts_CodepointToGlyph (kb_font, buffer->info[i].codepoint); - - if (num_features) { - for (unsigned int i = 0; i < num_features; ++i) + kbts_glyph_config *kb_config = nullptr; + if (num_features) { - hb_feature_t feature = features[i]; - for (unsigned int j = 0; j < buffer->len; ++j) + hb_vector_t<kbts_feature_override> kb_features; + for (unsigned int j = 0; j < num_features; ++j) { - kbts_glyph *kb_glyph = &kb_glyphs.arrayZ[j]; - if (hb_in_range (j, feature.start, feature.end)) - { - if (!kb_glyph->Config) - kb_glyph->Config = (kbts_glyph_config *) hb_calloc (1, sizeof (kbts_glyph_config)); - kbts_glyph_config *config = kb_glyph->Config; - while (!kbts_GlyphConfigOverrideFeatureFromTag (config, hb_uint32_swap (feature.tag), - feature.value > 1, feature.value)) - { - config->FeatureOverrides = (kbts_feature_override *) hb_realloc (config->FeatureOverrides, - config->RequiredFeatureOverrideCapacity); - config->FeatureOverrideCapacity += 1; - } - } + if (hb_in_range<size_t> (i, features[j].start, features[j].end)) + kb_features.push<kbts_feature_override>({ hb_uint32_swap (features[j].tag), (int)features[j].value }); } + if (kb_features) + kb_config = kbts_CreateGlyphConfig (kb_features.arrayZ, kb_features.length, nullptr, nullptr); } + kbts_PushGlyph (&kb_glyph_storage, kb_font, buffer->info[i].codepoint, kb_config, buffer->info[i].cluster); } - kbts_shape_state *kb_shape_state; - { - size_t kb_shape_state_size = kbts_SizeOfShapeState (kb_font); - void *kb_shape_state_buffer = hb_malloc (kb_shape_state_size); - if (unlikely (!kb_shape_state_buffer)) - { - DEBUG_MSG (KBTS, face, "Failed to allocate memory for shape state"); - return false; - } - kb_shape_state = kbts_PlaceShapeState (kb_shape_state_buffer, kb_shape_state_size); - } - kbts_shape_config kb_shape_config = kbts_ShapeConfig (kb_font, kb_script, kb_language); - uint32_t glyph_count = buffer->len; - uint32_t glyph_capacity = kb_glyphs.length; - while (kbts_Shape (kb_shape_state, &kb_shape_config, KBTS_DIRECTION_LTR, kb_direction, - kb_glyphs.arrayZ, &glyph_count, glyph_capacity)) - { - glyph_capacity = kb_shape_state->RequiredGlyphCapacity; - /* kb increases capacity by a fixed number only. We increase it by 50% to - * avoid O(n^2) behavior in case of expanding text. - * - * https://github.com/JimmyLefevre/kb/issues/32 - */ - glyph_capacity += glyph_capacity / 2; - if (unlikely (!kb_glyphs.resize_full (glyph_capacity, false, true))) - return false; - } + uint32_t glyph_count = 0; + kbts_glyph *kb_glyph = nullptr; + kbts_glyph_iterator kb_output; + hb_glyph_info_t *info; + hb_glyph_position_t *pos; + + kbts_shape_config *kb_shape_config = kbts_CreateShapeConfig (kb_font, kb_script, kb_language, nullptr, nullptr); + hb_bool_t res = kbts_ShapeDirect (kb_shape_config, &kb_glyph_storage, kb_direction, + nullptr, nullptr, &kb_output) == KBTS_SHAPE_ERROR_NONE; + if (unlikely (!res)) + goto done; + + for (auto it = kb_output; kbts_GlyphIteratorNext (&it, &kb_glyph); ) + glyph_count += 1; hb_buffer_set_content_type (buffer, HB_BUFFER_CONTENT_TYPE_GLYPHS); hb_buffer_set_length (buffer, glyph_count); - hb_glyph_info_t *info = buffer->info; - hb_glyph_position_t *pos = buffer->pos; - buffer->clear_positions (); - for (size_t i = 0; i < glyph_count; ++i) + + info = buffer->info; + pos = buffer->pos; + + for (auto it = kb_output; kbts_GlyphIteratorNext (&it, &kb_glyph); info++, pos++) { - kbts_glyph kb_glyph = kb_glyphs.arrayZ[i]; - info[i].codepoint = kb_glyph.Id; - info[i].cluster = 0; // FIXME - pos[i].x_advance = font->em_scalef_x (kb_glyph.AdvanceX); - pos[i].y_advance = font->em_scalef_y (kb_glyph.AdvanceY); - pos[i].x_offset = font->em_scalef_x (kb_glyph.OffsetX); - pos[i].y_offset = font->em_scalef_y (kb_glyph.OffsetY); - - if (kb_glyph.Config) - hb_free (kb_glyph.Config->FeatureOverrides); + info->codepoint = kb_glyph->Id; + info->cluster = kb_glyph->UserIdOrCodepointIndex; + pos->x_advance = font->em_scalef_x (kb_glyph->AdvanceX); + pos->y_advance = font->em_scalef_y (kb_glyph->AdvanceY); + pos->x_offset = font->em_scalef_x (kb_glyph->OffsetX); + pos->y_offset = font->em_scalef_y (kb_glyph->OffsetY); } - hb_free (kb_shape_state); +done: + kbts_DestroyShapeConfig (kb_shape_config); + while (kbts_GlyphIteratorNext (&kb_output, &kb_glyph)) + kbts_DestroyGlyphConfig (kb_glyph->Config); + kbts_FreeAllGlyphs (&kb_glyph_storage); buffer->clear_glyph_flags (); buffer->unsafe_to_break (); - return true; + return res; } #endif \ No newline at end of file diff --git a/gfx/harfbuzz/src/hb-open-file.hh b/gfx/harfbuzz/src/hb-open-file.hh @@ -465,11 +465,11 @@ struct OpenTypeFontFile Typ1Tag = HB_TAG ('t','y','p','1') /* Obsolete Apple Type1 font in SFNT container */ }; - hb_tag_t get_tag () const { return u.tag; } + hb_tag_t get_tag () const { return u.tag.v; } unsigned int get_face_count () const { - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -483,7 +483,7 @@ struct OpenTypeFontFile { if (base_offset) *base_offset = 0; - switch (u.tag) { + switch (u.tag.v) { /* Note: for non-collection SFNT data we ignore index. This is because * Apple dfont container is a container of SFNT's. So each SFNT is a * non-TTC, but the index is more than zero. */ @@ -512,9 +512,9 @@ struct OpenTypeFontFile bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.tag.sanitize (c))) return_trace (false); + if (unlikely (!u.tag.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.tag) { + switch (u.tag.v) { case CFFTag: /* All the non-collection tags */ case TrueTag: case Typ1Tag: @@ -527,13 +527,13 @@ struct OpenTypeFontFile protected: union { - Tag tag; /* 4-byte identifier. */ + struct { Tag v; } tag; /* 4-byte identifier. */ OpenTypeFontFace fontFace; TTCHeader ttcHeader; ResourceForkHeader rfHeader; } u; public: - DEFINE_SIZE_UNION (4, tag); + DEFINE_SIZE_UNION (4, tag.v); }; diff --git a/gfx/harfbuzz/src/hb-open-type.hh b/gfx/harfbuzz/src/hb-open-type.hh @@ -62,7 +62,7 @@ struct NumType typedef Type type; /* For reason we define cast out operator for signed/unsigned, instead of Type, see: * https://github.com/harfbuzz/harfbuzz/pull/2875/commits/09836013995cab2b9f07577a179ad7b024130467 */ - typedef typename std::conditional<std::is_integral<Type>::value, + typedef typename std::conditional<std::is_integral<Type>::value && sizeof (Type) <= sizeof(int), typename std::conditional<std::is_signed<Type>::value, signed, unsigned>::type, Type>::type WideType; @@ -118,6 +118,8 @@ typedef NumType<true, uint16_t> HBUINT16; /* 16-bit big-endian unsigned integer. typedef NumType<true, int16_t> HBINT16; /* 16-bit big-endian signed integer. */ typedef NumType<true, uint32_t> HBUINT32; /* 32-bit big-endian unsigned integer. */ typedef NumType<true, int32_t> HBINT32; /* 32-bit big-endian signed integer. */ +typedef NumType<true, uint64_t> HBUINT64; /* 64-bit big-endian unsigned integer. */ +typedef NumType<true, int64_t> HBINT64; /* 64-bit big-endian signed integer. */ /* Note: we cannot defined a signed HBINT24 because there's no corresponding C type. * Works for unsigned, but not signed, since we rely on compiler for sign-extension. */ typedef NumType<true, uint32_t, 3> HBUINT24; /* 24-bit big-endian unsigned integer. */ @@ -126,6 +128,8 @@ typedef NumType<false, uint16_t> HBUINT16LE; /* 16-bit little-endian unsigned in typedef NumType<false, int16_t> HBINT16LE; /* 16-bit little-endian signed integer. */ typedef NumType<false, uint32_t> HBUINT32LE; /* 32-bit little-endian unsigned integer. */ typedef NumType<false, int32_t> HBINT32LE; /* 32-bit little-endian signed integer. */ +typedef NumType<false, uint64_t> HBUINT64LE; /* 64-bit little-endian unsigned integer. */ +typedef NumType<false, int64_t> HBINT64LE; /* 64-bit little-endian signed integer. */ typedef NumType<true, float> HBFLOAT32BE; /* 32-bit little-endian floating point number. */ typedef NumType<true, double> HBFLOAT64BE; /* 64-bit little-endian floating point number. */ @@ -522,16 +526,9 @@ struct OffsetTo : Offset<OffsetType, has_null> return_trace (sanitize_shallow (c, base) && hb_barrier () && (this->is_null () || - c->dispatch (StructAtOffset<Type> (base, *this), std::forward<Ts> (ds)...) || - neuter (c))); + c->dispatch (StructAtOffset<Type> (base, *this), std::forward<Ts> (ds)...))); } - /* Set the offset to Null */ - bool neuter (hb_sanitize_context_t *c) const - { - if (!has_null) return false; - return c->try_set (this, 0); - } DEFINE_SIZE_STATIC (sizeof (OffsetType)); }; /* Partial specializations. */ @@ -1718,6 +1715,9 @@ struct TupleValues } template <typename T> +#ifndef HB_OPTIMIZE_SIZE + HB_ALWAYS_INLINE +#endif static bool decompile (const HBUINT8 *&p /* IN/OUT */, hb_vector_t<T> &values /* IN/OUT */, const HBUINT8 *end, @@ -1746,8 +1746,8 @@ struct TupleValues if ((control & VALUES_SIZE_MASK) == VALUES_ARE_ZEROS) { - for (; i < stop; i++) - values.arrayZ[i] = 0; + hb_memset (&values.arrayZ[i], 0, (stop - i) * sizeof (T)); + i = stop; } else if ((control & VALUES_SIZE_MASK) == VALUES_ARE_WORDS) { @@ -1806,7 +1806,7 @@ struct TupleValues { iter_t (const unsigned char *p_, unsigned len_) : p (p_), endp (p_ + len_) - { if (ensure_run ()) read_value (); } + { if (likely (ensure_run ())) read_value (); } private: const unsigned char *p; @@ -1815,10 +1815,14 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { if (likely (run_count > 0)) return true; - + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= endp)) { run_count = 0; @@ -1908,10 +1912,15 @@ struct TupleValues signed run_count = 0; unsigned width = 0; + HB_ALWAYS_INLINE bool ensure_run () { - if (run_count > 0) return true; + if (likely (run_count > 0)) return true; + return _ensure_run (); + } + bool _ensure_run () + { if (unlikely (p >= end)) { run_count = 0; @@ -2035,7 +2044,10 @@ struct TupleValues } #ifndef HB_OPTIMIZE_SIZE - if (scale == 1.0f) + // The following branch is supposed to speed things up by avoiding + // the multiplication in _add_to<> if scale is 1.0f. + // But in practice it seems to bloat the code and slow things down. + if (false && scale == 1.0f) _add_to<false> (out); else #endif diff --git a/gfx/harfbuzz/src/hb-ot-cmap-table.hh b/gfx/harfbuzz/src/hb-ot-cmap-table.hh @@ -697,16 +697,7 @@ struct CmapSubtableFormat4 hb_barrier (); if (unlikely (!c->check_range (this, length))) - { - /* Some broken fonts have too long of a "length" value. - * If that is the case, just change the value to truncate - * the subtable at the end of the blob. */ - uint16_t new_length = (uint16_t) hb_min ((uintptr_t) 65535, - (uintptr_t) (c->end - - (char *) this)); - if (!c->try_set (&length, new_length)) - return_trace (false); - } + return_trace (false); return_trace (16 + 4 * (unsigned int) segCountX2 <= length); } @@ -1496,7 +1487,7 @@ struct CmapSubtable bool get_glyph (hb_codepoint_t codepoint, hb_codepoint_t *glyph) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_glyph (codepoint, glyph); case 4: hb_barrier (); return u.format4 .get_glyph (codepoint, glyph); case 6: hb_barrier (); return u.format6 .get_glyph (codepoint, glyph); @@ -1509,7 +1500,7 @@ struct CmapSubtable } void collect_unicodes (hb_set_t *out, unsigned int num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_unicodes (out); return; case 4: hb_barrier (); u.format4 .collect_unicodes (out); return; case 6: hb_barrier (); u.format6 .collect_unicodes (out); return; @@ -1525,7 +1516,7 @@ struct CmapSubtable hb_map_t *mapping, /* OUT */ unsigned num_glyphs = UINT_MAX) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); u.format0 .collect_mapping (unicodes, mapping); return; case 4: hb_barrier (); u.format4 .collect_mapping (unicodes, mapping); return; case 6: hb_barrier (); u.format6 .collect_mapping (unicodes, mapping); return; @@ -1539,7 +1530,7 @@ struct CmapSubtable unsigned get_language () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0 .get_language (); case 4: hb_barrier (); return u.format4 .get_language (); case 6: hb_barrier (); return u.format6 .get_language (); @@ -1570,9 +1561,9 @@ struct CmapSubtable bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0 .sanitize (c)); case 4: hb_barrier (); return_trace (u.format4 .sanitize (c)); case 6: hb_barrier (); return_trace (u.format6 .sanitize (c)); @@ -1586,7 +1577,7 @@ struct CmapSubtable public: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ CmapSubtableFormat0 format0; CmapSubtableFormat4 format4; CmapSubtableFormat6 format6; @@ -1596,7 +1587,7 @@ struct CmapSubtable CmapSubtableFormat14 format14; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; @@ -1806,7 +1797,7 @@ struct cmap if (c->in_error ()) return false; - unsigned format = (base+_.subtable).u.format; + unsigned format = (base+_.subtable).u.format.v; if (format != 4 && format != 12 && format != 14) continue; const hb_set_t* unicodes_set = unicodes_cache->set_for (&_, local_unicodes_cache); @@ -1908,7 +1899,7 @@ struct cmap + hb_iter (encodingRecord) | hb_map (&EncodingRecord::subtable) | hb_map (hb_add (this)) - | hb_filter ([&] (const CmapSubtable& _) { return _.u.format == 14; }) + | hb_filter ([&] (const CmapSubtable& _) { return _.u.format.v == 14; }) | hb_apply ([=] (const CmapSubtable& _) { _.u.format14.closure_glyphs (unicodes, glyphset); }) ; } @@ -1933,7 +1924,7 @@ struct cmap for (const EncodingRecord& _ : encodingrec_iter) { - unsigned format = (this + _.subtable).u.format; + unsigned format = (this + _.subtable).u.format.v; if (format == 12) has_format12 = true; const EncodingRecord *table = std::addressof (_); @@ -2021,7 +2012,7 @@ struct cmap this->subtable_uvs = &Null (CmapSubtableFormat14); { const CmapSubtable *st = table->find_subtable (0, 5); - if (st && st->u.format == 14) + if (st && st->u.format.v == 14) subtable_uvs = &st->u.format14; } @@ -2065,7 +2056,7 @@ struct cmap else #endif { - switch (subtable->u.format) { + switch (subtable->u.format.v) { /* Accelerate format 4 and format 12. */ default: this->get_glyph_funcZ = get_glyph_from<CmapSubtable>; @@ -2272,7 +2263,7 @@ struct cmap (_.platformID == 0 && _.encodingID == 4) || (_.platformID == 3 && _.encodingID == 1) || (_.platformID == 3 && _.encodingID == 10) || - (cmap + _.subtable).u.format == 14; + (cmap + _.subtable).u.format.v == 14; } protected: diff --git a/gfx/harfbuzz/src/hb-ot-kern-table.hh b/gfx/harfbuzz/src/hb-ot-kern-table.hh @@ -306,8 +306,8 @@ struct kern { static constexpr hb_tag_t tableTag = HB_OT_TAG_kern; - bool has_data () const { return u.version32; } - unsigned get_type () const { return u.major; } + bool has_data () const { return u.version32.v; } + unsigned get_type () const { return u.major.v; } bool has_state_machine () const { @@ -363,7 +363,7 @@ struct kern bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.version32.sanitize (c)) return_trace (false); + if (!u.version32.v.sanitize (c)) return_trace (false); hb_barrier (); return_trace (dispatch (c)); } @@ -406,15 +406,15 @@ struct kern protected: union { - HBUINT32 version32; - HBUINT16 major; + struct { HBUINT32 v; } version32; + struct { HBUINT16 v; } major; KernOT ot; #ifndef HB_NO_AAT_SHAPE KernAAT aat; #endif } u; public: - DEFINE_SIZE_UNION (4, version32); + DEFINE_SIZE_UNION (4, version32.v); }; struct kern_accelerator_t : kern::accelerator_t { diff --git a/gfx/harfbuzz/src/hb-ot-layout-base-table.hh b/gfx/harfbuzz/src/hb-ot-layout-base-table.hh @@ -165,13 +165,13 @@ struct BaseCoordFormat3 struct BaseCoord { - bool has_data () const { return u.format; } + bool has_data () const { return u.format.v; } hb_position_t get_coord (hb_font_t *font, const ItemVariationStore &var_store, hb_direction_t direction) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_coord (font, direction); case 2: hb_barrier (); return u.format2.get_coord (font, direction); case 3: hb_barrier (); return u.format3.get_coord (font, var_store, direction); @@ -181,7 +181,7 @@ struct BaseCoord void collect_variation_indices (hb_set_t& varidx_set /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 3: hb_barrier (); u.format3.collect_variation_indices (varidx_set); return; default:return; } @@ -190,9 +190,9 @@ struct BaseCoord template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -203,9 +203,9 @@ struct BaseCoord bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (unlikely (!u.format.sanitize (c))) return_trace (false); + if (unlikely (!u.format.v.sanitize (c))) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -215,13 +215,13 @@ struct BaseCoord protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; BaseCoordFormat1 format1; BaseCoordFormat2 format2; BaseCoordFormat3 format3; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct FeatMinMaxRecord diff --git a/gfx/harfbuzz/src/hb-ot-layout-common.hh b/gfx/harfbuzz/src/hb-ot-layout-common.hh @@ -828,46 +828,9 @@ struct Feature const Record_sanitize_closure_t *closure = nullptr) const { TRACE_SANITIZE (this); - if (unlikely (!(c->check_struct (this) && lookupIndex.sanitize (c)))) - return_trace (false); - hb_barrier (); - - /* Some earlier versions of Adobe tools calculated the offset of the - * FeatureParams subtable from the beginning of the FeatureList table! - * - * If sanitizing "failed" for the FeatureParams subtable, try it with the - * alternative location. We would know sanitize "failed" if old value - * of the offset was non-zero, but it's zeroed now. - * - * Only do this for the 'size' feature, since at the time of the faulty - * Adobe tools, only the 'size' feature had FeatureParams defined. - */ - - if (likely (featureParams.is_null ())) - return_trace (true); - - unsigned int orig_offset = featureParams; - if (unlikely (!featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE))) - return_trace (false); - hb_barrier (); - - if (featureParams == 0 && closure && - closure->tag == HB_TAG ('s','i','z','e') && - closure->list_base && closure->list_base < this) - { - unsigned int new_offset_int = orig_offset - - (((char *) this) - ((char *) closure->list_base)); - - Offset16To<FeatureParams> new_offset; - /* Check that it would not overflow. */ - new_offset = new_offset_int; - if (new_offset == new_offset_int && - c->try_set (&featureParams, new_offset_int) && - !featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE)) - return_trace (false); - } - - return_trace (true); + return_trace (c->check_struct (this) && + featureParams.sanitize (c, this, closure ? closure->tag : HB_TAG_NONE) && + lookupIndex.sanitize (c)); } Offset16To<FeatureParams> @@ -1433,7 +1396,7 @@ struct Lookup if (unlikely (!get_subtables<TSubTable> ().sanitize (c, this, get_type ()))) return_trace (false); - if (unlikely (get_type () == TSubTable::Extension && !c->get_edit_count ())) + if (unlikely (get_type () == TSubTable::Extension)) { hb_barrier (); @@ -1441,11 +1404,6 @@ struct Lookup * have the same type, which shall not be the Extension type * itself (but we already checked for that). * This is specially important if one has a reverse type! - * - * We only do this if sanitizer edit_count is zero. Otherwise, - * some of the subtables might have become insane after they - * were sanity-checked by the edits of subsequent subtables. - * https://bugs.chromium.org/p/chromium/issues/detail?id=960331 */ unsigned int type = get_subtable<TSubTable> (0).u.extension.get_type (); for (unsigned int i = 1; i < subtables; i++) @@ -2075,7 +2033,7 @@ struct ClassDef unsigned int get (hb_codepoint_t k) const { return get_class (k); } unsigned int get_class (hb_codepoint_t glyph_id) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_class (glyph_id); case 2: hb_barrier (); return u.format2.get_class (glyph_id); #ifndef HB_NO_BEYOND_64K @@ -2097,7 +2055,7 @@ struct ClassDef unsigned get_population () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_population (); case 2: hb_barrier (); return u.format2.get_population (); #ifndef HB_NO_BEYOND_64K @@ -2150,7 +2108,7 @@ struct ClassDef #ifndef HB_NO_BEYOND_64K if (glyph_max > 0xFFFFu) - u.format += 2; + u.format.v += 2; if (unlikely (glyph_max > 0xFFFFFFu)) #else if (unlikely (glyph_max > 0xFFFFu)) @@ -2160,9 +2118,9 @@ struct ClassDef return_trace (false); } - u.format = format; + u.format.v = format; - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.serialize (c, it)); case 2: hb_barrier (); return_trace (u.format2.serialize (c, it)); @@ -2181,7 +2139,7 @@ struct ClassDef const Coverage* glyph_filter = nullptr) const { TRACE_SUBSET (this); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); case 2: hb_barrier (); return_trace (u.format2.subset (c, klass_map, keep_empty_table, use_class_zero, glyph_filter)); #ifndef HB_NO_BEYOND_64K @@ -2195,9 +2153,9 @@ struct ClassDef bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); #ifndef HB_NO_BEYOND_64K @@ -2210,7 +2168,7 @@ struct ClassDef unsigned cost () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.cost (); case 2: hb_barrier (); return u.format2.cost (); #ifndef HB_NO_BEYOND_64K @@ -2226,7 +2184,7 @@ struct ClassDef template <typename set_t> bool collect_coverage (set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_coverage (glyphs); case 2: hb_barrier (); return u.format2.collect_coverage (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2242,7 +2200,7 @@ struct ClassDef template <typename set_t> bool collect_class (set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.collect_class (glyphs, klass); case 2: hb_barrier (); return u.format2.collect_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2255,7 +2213,7 @@ struct ClassDef bool intersects (const hb_set_t *glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects (glyphs); case 2: hb_barrier (); return u.format2.intersects (glyphs); #ifndef HB_NO_BEYOND_64K @@ -2267,7 +2225,7 @@ struct ClassDef } bool intersects_class (const hb_set_t *glyphs, unsigned int klass) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersects_class (glyphs, klass); case 2: hb_barrier (); return u.format2.intersects_class (glyphs, klass); #ifndef HB_NO_BEYOND_64K @@ -2280,7 +2238,7 @@ struct ClassDef void intersected_class_glyphs (const hb_set_t *glyphs, unsigned klass, hb_set_t *intersect_glyphs) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_class_glyphs (glyphs, klass, intersect_glyphs); case 2: hb_barrier (); return u.format2.intersected_class_glyphs (glyphs, klass, intersect_glyphs); #ifndef HB_NO_BEYOND_64K @@ -2293,7 +2251,7 @@ struct ClassDef void intersected_classes (const hb_set_t *glyphs, hb_set_t *intersect_classes) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.intersected_classes (glyphs, intersect_classes); case 2: hb_barrier (); return u.format2.intersected_classes (glyphs, intersect_classes); #ifndef HB_NO_BEYOND_64K @@ -2307,7 +2265,7 @@ struct ClassDef protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ClassDefFormat1_3<SmallTypes> format1; ClassDefFormat2_4<SmallTypes> format2; #ifndef HB_NO_BEYOND_64K @@ -2316,7 +2274,7 @@ struct ClassDef #endif } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template<typename Iterator> @@ -2628,7 +2586,17 @@ struct hb_scalar_cache_t void clear () { auto *values = &static_values[0]; - for (unsigned i = 0; i < length; i++) + unsigned i = 0; +#ifndef HB_OPTIMIZE_SIZE + for (; i + 3 < length; i += 4) + { + values[i + 0] = INVALID; + values[i + 1] = INVALID; + values[i + 2] = INVALID; + values[i + 3] = INVALID; + } +#endif + for (; i < length; i++) values[i] = INVALID; } @@ -2642,12 +2610,17 @@ struct hb_scalar_cache_t } auto *values = &static_values[0]; auto *cached_value = &values[i]; - if (*cached_value != INVALID) + // Super hot. Most common path is that we have a cached value of 0. + int v = *cached_value; + if (likely (!v)) { - *value = *cached_value ? *cached_value * DIVISOR : 0.f; + *value = 0.f; return true; } - return false; + if (v == INVALID) + return false; + *value = v * DIVISOR; + return true; } HB_ALWAYS_INLINE @@ -3763,8 +3736,8 @@ struct DeltaSetIndexMap { TRACE_SERIALIZE (this); unsigned length = plan.get_output_map ().length; - u.format = length <= 0xFFFF ? 0 : 1; - switch (u.format) { + u.format.v = length <= 0xFFFF ? 0 : 1; + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.serialize (c, plan)); case 1: hb_barrier (); return_trace (u.format1.serialize (c, plan)); default:return_trace (false); @@ -3773,7 +3746,7 @@ struct DeltaSetIndexMap uint32_t map (unsigned v) const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return (u.format0.map (v)); case 1: hb_barrier (); return (u.format1.map (v)); default:return v; @@ -3782,7 +3755,7 @@ struct DeltaSetIndexMap unsigned get_map_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_map_count (); case 1: hb_barrier (); return u.format1.get_map_count (); default:return 0; @@ -3791,7 +3764,7 @@ struct DeltaSetIndexMap unsigned get_width () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_width (); case 1: hb_barrier (); return u.format1.get_width (); default:return 0; @@ -3800,7 +3773,7 @@ struct DeltaSetIndexMap unsigned get_inner_bit_count () const { - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return u.format0.get_inner_bit_count (); case 1: hb_barrier (); return u.format1.get_inner_bit_count (); default:return 0; @@ -3810,9 +3783,9 @@ struct DeltaSetIndexMap bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (u.format0.sanitize (c)); case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); default:return_trace (true); @@ -3822,7 +3795,7 @@ struct DeltaSetIndexMap DeltaSetIndexMap* copy (hb_serialize_context_t *c) const { TRACE_SERIALIZE (this); - switch (u.format) { + switch (u.format.v) { case 0: hb_barrier (); return_trace (reinterpret_cast<DeltaSetIndexMap *> (u.format0.copy (c))); case 1: hb_barrier (); return_trace (reinterpret_cast<DeltaSetIndexMap *> (u.format1.copy (c))); default:return_trace (nullptr); @@ -3831,12 +3804,12 @@ struct DeltaSetIndexMap protected: union { - HBUINT8 format; /* Format identifier */ + struct { HBUINT8 v; } format; /* Format identifier */ DeltaSetIndexMapFormat01<HBUINT16> format0; DeltaSetIndexMapFormat01<HBUINT32> format1; } u; public: - DEFINE_SIZE_UNION (1, format); + DEFINE_SIZE_UNION (1, format.v); }; @@ -4239,7 +4212,7 @@ struct Condition bool evaluate (const int *coords, unsigned int coord_len, Instancer *instancer) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.evaluate (coords, coord_len, instancer); case 2: hb_barrier (); return u.format2.evaluate (coords, coord_len, instancer); case 3: hb_barrier (); return u.format3.evaluate (coords, coord_len, instancer); @@ -4252,7 +4225,7 @@ struct Condition Cond_with_Var_flag_t keep_with_variations (hb_collect_feature_substitutes_with_var_context_t *c, hb_map_t *condition_map /* OUT */) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_with_variations (c, condition_map); // TODO(subset) default: c->apply = false; return KEEP_COND_WITH_VAR; @@ -4262,9 +4235,9 @@ struct Condition template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -4277,9 +4250,9 @@ struct Condition bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - if (!u.format.sanitize (c)) return_trace (false); + if (!u.format.v.sanitize (c)) return_trace (false); hb_barrier (); - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); case 3: hb_barrier (); return_trace (u.format3.sanitize (c)); @@ -4291,7 +4264,7 @@ struct Condition protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ConditionAxisRange format1; ConditionValue format2; ConditionAnd format3; @@ -4299,7 +4272,7 @@ struct Condition ConditionNegate format5; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; template <typename Instancer> diff --git a/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh b/gfx/harfbuzz/src/hb-ot-layout-gsubgpos.hh @@ -1312,10 +1312,19 @@ static bool match_input (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); - if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); - hb_buffer_t *buffer = c->buffer; + if (count == 1) + { + *end_position = buffer->idx + 1; + c->match_positions[0] = buffer->idx; + if (p_total_component_count) + *p_total_component_count = _hb_glyph_info_get_lig_num_comps (&buffer->cur()); + return_trace (true); + } + + if (unlikely (count > HB_MAX_CONTEXT_LENGTH)) return_trace (false); + auto &skippy_iter = c->iter_input; skippy_iter.reset (buffer->idx); skippy_iter.set_match_func (match_func, match_data); @@ -1560,6 +1569,12 @@ static bool match_backtrack (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); + if (!count) + { + *match_start = c->buffer->backtrack_len (); + return_trace (true); + } + auto &skippy_iter = c->iter_context; skippy_iter.reset_back (c->buffer->backtrack_len ()); skippy_iter.set_match_func (match_func, match_data); @@ -1593,6 +1608,12 @@ static bool match_lookahead (hb_ot_apply_context_t *c, { TRACE_APPLY (nullptr); + if (!count) + { + *end_index = start_index; + return_trace (true); + } + auto &skippy_iter = c->iter_context; assert (start_index >= 1); skippy_iter.reset (start_index - 1); @@ -2246,7 +2267,12 @@ struct RuleSet * * Replicated from LigatureSet::apply(). */ - auto &skippy_iter = c->iter_input; + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. + * + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 + */ + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); @@ -2255,15 +2281,15 @@ struct RuleSet bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to = skippy_iter.idx + 1; } else { @@ -2279,8 +2305,15 @@ struct RuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -2317,6 +2350,15 @@ struct RuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = r2.inputZ; + if (r2.inputCount <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -2666,12 +2708,30 @@ struct ContextFormat2_5 return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c, void *external_cache HB_UNUSED) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &class_def = this+classDef; @@ -2926,9 +2986,9 @@ struct Context template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -2942,7 +3002,7 @@ struct Context protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ContextFormat1_4<SmallTypes> format1; ContextFormat2_5<SmallTypes> format2; ContextFormat3 format3; @@ -3404,16 +3464,12 @@ struct ChainRuleSet * * Replicated from LigatureSet::apply(). */ - /* If the input skippy has non-auto joiners behavior (as in Indic shapers), - * skip this fast path, as we don't distinguish between input & lookahead - * matching in the fast path. + /* We use the iter_context instead of iter_input, to avoid skipping + * default-ignorables and such. * - * https://github.com/harfbuzz/harfbuzz/issues/4813 + * Related: https://github.com/harfbuzz/harfbuzz/issues/4813 */ - if (!c->auto_zwnj || !c->auto_zwj) - goto slow; - - auto &skippy_iter = c->iter_input; + auto &skippy_iter = c->iter_context; skippy_iter.reset (c->buffer->idx); skippy_iter.set_match_func (match_always, nullptr); skippy_iter.set_glyph_data ((HBUINT16 *) nullptr); @@ -3422,15 +3478,15 @@ struct ChainRuleSet bool matched = skippy_iter.next (); if (likely (matched)) { - first = &c->buffer->info[skippy_iter.idx]; - unsafe_to1 = skippy_iter.idx + 1; - if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) { /* Can't use the fast path if eg. the next char is a default-ignorable * or other skippable. */ goto slow; } + + first = &c->buffer->info[skippy_iter.idx]; + unsafe_to1 = skippy_iter.idx + 1; } else { @@ -3451,8 +3507,15 @@ struct ChainRuleSet ; } matched = skippy_iter.next (); - if (likely (matched && !skippy_iter.may_skip (c->buffer->info[skippy_iter.idx]))) + if (likely (matched)) { + if (skippy_iter.may_skip (c->buffer->info[skippy_iter.idx])) + { + /* Can't use the fast path if eg. the next char is a default-ignorable + * or other skippable. */ + goto slow; + } + second = &c->buffer->info[skippy_iter.idx]; unsafe_to2 = skippy_iter.idx + 1; } @@ -3499,6 +3562,18 @@ struct ChainRuleSet { if (unsafe_to == (unsigned) -1) unsafe_to = unsafe_to1; + + if (lenP1 > 1) + { + // Skip ahead to next possible first glyph match. + for (; i + 1 < num_rules; i++) + { + const auto &r2 = this+rule.arrayZ[i + 1]; + const auto &input2 = StructAfter<decltype (r2.inputX)> (r2.backtrack); + if (input2.lenP1 <= 1 || input2.arrayZ[0] != input.arrayZ[0]) + break; + } + } } } if (likely (unsafe_to != (unsigned) -1)) @@ -3874,12 +3949,30 @@ struct ChainContextFormat2_5 return context_cache_func (c, op); } - bool apply_cached (hb_ot_apply_context_t *c, void *external_cache HB_UNUSED) const { return _apply (c, true); } - bool apply (hb_ot_apply_context_t *c) const { return _apply (c, false); } - bool _apply (hb_ot_apply_context_t *c, bool cached) const + struct external_cache_t + { + hb_ot_layout_binary_cache_t coverage; + }; + void *external_cache_create () const + { + external_cache_t *cache = (external_cache_t *) hb_malloc (sizeof (external_cache_t)); + if (likely (cache)) + { + cache->coverage.clear (); + } + return cache; + } + bool apply_cached (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, true, external_cache); } + bool apply (hb_ot_apply_context_t *c, void *external_cache) const { return _apply (c, false, external_cache); } + bool _apply (hb_ot_apply_context_t *c, bool cached, void *external_cache) const { TRACE_APPLY (this); +#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE + external_cache_t *cache = (external_cache_t *) external_cache; + unsigned int index = (this+coverage).get_coverage_binary (c->buffer->cur().codepoint, cache ? &cache->coverage : nullptr); +#else unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); +#endif if (index == NOT_COVERED) return_trace (false); const ClassDef &backtrack_class_def = this+backtrackClassDef; @@ -4227,9 +4320,9 @@ struct ChainContext template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -4243,7 +4336,7 @@ struct ChainContext protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ChainContextFormat1_4<SmallTypes> format1; ChainContextFormat2_5<SmallTypes> format2; ChainContextFormat3 format3; @@ -4318,7 +4411,7 @@ struct Extension { unsigned int get_type () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_type (); default:return 0; } @@ -4326,7 +4419,7 @@ struct Extension template <typename X> const X& get_subtable () const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.template get_subtable<typename T::SubTable> (); default:return Null (typename T::SubTable); } @@ -4338,7 +4431,7 @@ struct Extension template <typename ...Ts> typename hb_subset_context_t::return_t dispatch (hb_subset_context_t *c, Ts&&... ds) const { - switch (u.format) { + switch (u.format.v) { case 1: hb_barrier (); return u.format1.subset (c); default: return c->default_return_value (); } @@ -4347,9 +4440,9 @@ struct Extension template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.dispatch (c, std::forward<Ts> (ds)...)); default:return_trace (c->default_return_value ()); } @@ -4357,7 +4450,7 @@ struct Extension protected: union { - HBUINT16 format; /* Format identifier */ + struct { HBUINT16 v; } format; /* Format identifier */ ExtensionFormat1<T> format1; } u; }; diff --git a/gfx/harfbuzz/src/hb-ot-shaper-indic.cc b/gfx/harfbuzz/src/hb-ot-shaper-indic.cc @@ -296,11 +296,6 @@ struct indic_shape_plan_t const indic_config_t *config; bool is_old_spec; -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - bool uniscribe_bug_compatible; -#else - static constexpr bool uniscribe_bug_compatible = false; -#endif mutable hb_atomic_t<hb_codepoint_t> virama_glyph; hb_indic_would_substitute_feature_t rphf; @@ -327,9 +322,6 @@ data_create_indic (const hb_ot_shape_plan_t *plan) } indic_plan->is_old_spec = indic_plan->config->has_old_spec && ((plan->map.chosen_script[0] & 0x000000FFu) != '2'); -#ifndef HB_NO_UNISCRIBE_BUG_COMPATIBLE - indic_plan->uniscribe_bug_compatible = hb_options ().uniscribe_bug_compatible; -#endif indic_plan->virama_glyph = -1; /* Use zero-context would_substitute() matching for new-spec of the main @@ -943,17 +935,7 @@ initial_reordering_standalone_cluster (const hb_ot_shape_plan_t *plan, unsigned int start, unsigned int end) { /* We treat placeholder/dotted-circle as if they are consonants, so we - * should just chain. Only if not in compatibility mode that is... */ - - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (indic_plan->uniscribe_bug_compatible) - { - /* For dotted-circle, this is what Uniscribe does: - * If dotted-circle is the last glyph, it just does nothing. - * Ie. It doesn't form Reph. */ - if (buffer->info[end - 1].indic_category() == I_Cat(DOTTEDCIRCLE)) - return; - } + * should just chain... */ initial_reordering_consonant_syllable (plan, face, buffer, start, end); } @@ -1347,8 +1329,7 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, * Uniscribe doesn't do this. * TEST: U+0930,U+094D,U+0915,U+094B,U+094D */ - if (!indic_plan->uniscribe_bug_compatible && - unlikely (is_halant (info[new_reph_pos]))) + if (unlikely (is_halant (info[new_reph_pos]))) { for (unsigned int i = base + 1; i < new_reph_pos; i++) if (FLAG_UNSAFE (info[i].indic_category()) & (FLAG (I_Cat(M)) | FLAG (I_Cat(MPst)))) @@ -1451,27 +1432,6 @@ final_reordering_syllable_indic (const hb_ot_shape_plan_t *plan, else buffer->unsafe_to_break (start - 1, start + 1); } - - - /* - * Finish off the clusters and go home! - */ - if (indic_plan->uniscribe_bug_compatible) - { - switch ((hb_tag_t) plan->props.script) - { - case HB_SCRIPT_TAMIL: - break; - - default: - /* Uniscribe merges the entire syllable into a single cluster... Except for Tamil. - * This means, half forms are submerged into the main consonant's cluster. - * This is unnecessary, and makes cursor positioning harder, but that's what - * Uniscribe does. */ - buffer->merge_clusters (start, end); - break; - } - } } @@ -1501,9 +1461,7 @@ preprocess_text_indic (const hb_ot_shape_plan_t *plan, hb_buffer_t *buffer, hb_font_t *font) { - const indic_shape_plan_t *indic_plan = (const indic_shape_plan_t *) plan->data; - if (!indic_plan->uniscribe_bug_compatible) - _hb_preprocess_text_vowel_constraints (plan, buffer, font); + _hb_preprocess_text_vowel_constraints (plan, buffer, font); } static bool diff --git a/gfx/harfbuzz/src/hb-ot-shaper-khmer.cc b/gfx/harfbuzz/src/hb-ot-shaper-khmer.cc @@ -141,12 +141,6 @@ override_features_khmer (hb_ot_shape_planner_t *plan) * typographical correctness.", hence in overrides... */ map->enable_feature (HB_TAG('c','l','i','g')); - /* Uniscribe does not apply 'kern' in Khmer. */ - if (hb_options ().uniscribe_bug_compatible) - { - map->disable_feature (HB_TAG('k','e','r','n')); - } - map->disable_feature (HB_TAG('l','i','g','a')); } diff --git a/gfx/harfbuzz/src/hb-ot-stat-table.hh b/gfx/harfbuzz/src/hb-ot-stat-table.hh @@ -352,7 +352,7 @@ struct AxisValue { float get_value (unsigned int axis_index) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value (); case 2: hb_barrier (); return u.format2.get_value (); @@ -364,7 +364,7 @@ struct AxisValue unsigned int get_axis_index () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_axis_index (); case 2: hb_barrier (); return u.format2.get_axis_index (); @@ -376,7 +376,7 @@ struct AxisValue hb_ot_name_id_t get_value_name_id () const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.get_value_name_id (); case 2: hb_barrier (); return u.format2.get_value_name_id (); @@ -389,9 +389,9 @@ struct AxisValue template <typename context_t, typename ...Ts> typename context_t::return_t dispatch (context_t *c, Ts&&... ds) const { - if (unlikely (!c->may_dispatch (this, &u.format))) return c->no_dispatch_return_value (); - TRACE_DISPATCH (this, u.format); - switch (u.format) { + if (unlikely (!c->may_dispatch (this, &u.format.v))) return c->no_dispatch_return_value (); + TRACE_DISPATCH (this, u.format.v); + switch (u.format.v) { case 1: hb_barrier (); return_trace (c->dispatch (u.format1, std::forward<Ts> (ds)...)); case 2: hb_barrier (); return_trace (c->dispatch (u.format2, std::forward<Ts> (ds)...)); case 3: hb_barrier (); return_trace (c->dispatch (u.format3, std::forward<Ts> (ds)...)); @@ -403,7 +403,7 @@ struct AxisValue bool keep_axis_value (const hb_array_t<const StatAxisRecord> axis_records, hb_hashmap_t<hb_tag_t, Triple> *user_axes_location) const { - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return u.format1.keep_axis_value (axis_records, user_axes_location); case 2: hb_barrier (); return u.format2.keep_axis_value (axis_records, user_axes_location); @@ -420,7 +420,7 @@ struct AxisValue return_trace (false); hb_barrier (); - switch (u.format) + switch (u.format.v) { case 1: hb_barrier (); return_trace (u.format1.sanitize (c)); case 2: hb_barrier (); return_trace (u.format2.sanitize (c)); @@ -433,14 +433,14 @@ struct AxisValue protected: union { - HBUINT16 format; + struct { HBUINT16 v; } format; AxisValueFormat1 format1; AxisValueFormat2 format2; AxisValueFormat3 format3; AxisValueFormat4 format4; } u; public: - DEFINE_SIZE_UNION (2, format); + DEFINE_SIZE_UNION (2, format.v); }; struct AxisValueOffsetArray: UnsizedArrayOf<Offset16To<AxisValue>> diff --git a/gfx/harfbuzz/src/hb-ot-var-avar-table.hh b/gfx/harfbuzz/src/hb-ot-var-avar-table.hh @@ -411,6 +411,54 @@ struct avar #endif } + bool has_v2_data () const { return version.major > 1; } + + // axis normalization is done in 2.14 here + // TODO: deprecate this API once fonttools is updated to use 16.16 normalization + bool map_coords_2_14 (float *coords, unsigned int coords_length) const + { + hb_vector_t<int> coords_2_14; + if (!coords_2_14.resize (coords_length)) return false; + unsigned int count = hb_min (coords_length, axisCount); + + const SegmentMaps *map = &firstAxisSegmentMaps; + for (unsigned int i = 0; i < count; i++) + { + int v = roundf (map->map_float (coords[i]) * 16384.f); + coords_2_14[i] = v; + coords[i] = v / 16384.f; + map = &StructAfter<SegmentMaps> (*map); + } + +#ifndef HB_NO_AVAR2 + if (version.major < 2) + return true; + hb_barrier (); + + for (; count < axisCount; count++) + map = &StructAfter<SegmentMaps> (*map); + + const auto &v2 = * (const avarV2Tail *) map; + + const auto &varidx_map = this+v2.varIdxMap; + const auto &var_store = this+v2.varStore; + auto *var_store_cache = var_store.create_cache (); + + for (unsigned i = 0; i < coords_length; i++) + { + int v = coords_2_14[i]; + uint32_t varidx = varidx_map.map (i); + float delta = var_store.get_delta (varidx, coords_2_14.arrayZ, coords_2_14.length, var_store_cache); + v += roundf (delta); + v = hb_clamp (v, -(1<<16), +(1<<16)); + coords[i] = v / 16384.f; + } + + OT::ItemVariationStore::destroy_cache (var_store_cache); + return true; +#endif + } + bool subset (hb_subset_context_t *c) const { TRACE_SUBSET (this); diff --git a/gfx/harfbuzz/src/hb-ot-var-common.hh b/gfx/harfbuzz/src/hb-ot-var-common.hh @@ -40,13 +40,22 @@ using rebase_tent_result_scratch_t = hb_pair_t<rebase_tent_result_t, rebase_tent struct TupleVariationHeader { friend struct tuple_delta_t; - unsigned get_size (unsigned axis_count) const - { return min_size + get_all_tuples (axis_count).get_size (); } + unsigned get_size (unsigned axis_count_times_2) const + { + // This function is super hot in mega-var-fonts with hundreds of masters. + unsigned ti = tupleIndex; + if (unlikely ((ti & (TupleIndex::EmbeddedPeakTuple | TupleIndex::IntermediateRegion)))) + { + unsigned count = ((ti & TupleIndex::EmbeddedPeakTuple) != 0) + ((ti & TupleIndex::IntermediateRegion) != 0) * 2; + return min_size + count * axis_count_times_2; + } + return min_size; + } unsigned get_data_size () const { return varDataSize; } - const TupleVariationHeader &get_next (unsigned axis_count) const - { return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count)); } + const TupleVariationHeader &get_next (unsigned axis_count_times_2) const + { return StructAtOffset<TupleVariationHeader> (this, get_size (axis_count_times_2)); } bool unpack_axis_tuples (unsigned axis_count, const hb_array_t<const F2DOT14> shared_tuples, @@ -109,23 +118,26 @@ struct TupleVariationHeader const F2DOT14 *peak_tuple; - bool has_interm = tuple_index & TuppleIndex::IntermediateRegion; // Inlined for performance - if (unlikely (has_interm)) - shared_tuple_scalar_cache = nullptr; + bool has_interm = tuple_index & TupleIndex::IntermediateRegion; // Inlined for performance - if (unlikely (tuple_index & TuppleIndex::EmbeddedPeakTuple)) // Inlined for performance + if (unlikely (tuple_index & TupleIndex::EmbeddedPeakTuple)) // Inlined for performance { peak_tuple = get_peak_tuple (coord_count); shared_tuple_scalar_cache = nullptr; } else { - unsigned int index = tuple_index & TuppleIndex::TupleIndexMask; // Inlined for performance + unsigned int index = tuple_index & TupleIndex::TupleIndexMask; // Inlined for performance float scalar; if (shared_tuple_scalar_cache && shared_tuple_scalar_cache->get (index, &scalar)) - return (double) scalar; + { + if (has_interm && (scalar != 0 && scalar != 1.f)) + shared_tuple_scalar_cache = nullptr; + else + return (double) scalar; + } if (unlikely ((index + 1) * coord_count > shared_tuples.length)) return 0.0; @@ -143,8 +155,27 @@ struct TupleVariationHeader } double scalar = 1.0; +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + bool skip = coord_count >= 16; +#endif +#endif for (unsigned int i = 0; i < coord_count; i++) { +#ifndef HB_OPTIMIZE_SIZE +#if HB_FAST_NUM_ACCESS + if (skip) + { + while (i + 4 <= coord_count && * (HBUINT64LE *) &peak_tuple[i] == 0) + i += 4; + while (i < coord_count && peak_tuple[i].to_int () == 0) + i += 1; + if (i >= coord_count) + break; + } +#endif +#endif + int peak = peak_tuple[i].to_int (); if (!peak) continue; @@ -154,6 +185,7 @@ struct TupleVariationHeader if (has_interm) { + shared_tuple_scalar_cache = nullptr; int start = start_tuple[i].to_int (); int end = end_tuple[i].to_int (); if (unlikely (start > peak || peak > end || @@ -173,13 +205,13 @@ struct TupleVariationHeader return scalar; } - bool has_peak () const { return tupleIndex & TuppleIndex::EmbeddedPeakTuple; } - bool has_intermediate () const { return tupleIndex & TuppleIndex::IntermediateRegion; } - bool has_private_points () const { return tupleIndex & TuppleIndex::PrivatePointNumbers; } - unsigned get_index () const { return tupleIndex & TuppleIndex::TupleIndexMask; } + bool has_peak () const { return tupleIndex & TupleIndex::EmbeddedPeakTuple; } + bool has_intermediate () const { return tupleIndex & TupleIndex::IntermediateRegion; } + bool has_private_points () const { return tupleIndex & TupleIndex::PrivatePointNumbers; } + unsigned get_index () const { return tupleIndex & TupleIndex::TupleIndexMask; } protected: - struct TuppleIndex : HBUINT16 + struct TupleIndex : HBUINT16 { enum Flags { EmbeddedPeakTuple = 0x8000u, @@ -188,7 +220,7 @@ struct TupleVariationHeader TupleIndexMask = 0x0FFFu }; - TuppleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } + TupleIndex& operator = (uint16_t i) { HBUINT16::operator= (i); return *this; } DEFINE_SIZE_STATIC (2); }; @@ -205,7 +237,7 @@ struct TupleVariationHeader HBUINT16 varDataSize; /* The size in bytes of the serialized * data for this tuple variation table. */ - TuppleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). + TupleIndex tupleIndex; /* A packed field. The high 4 bits are flags (see below). The low 12 bits are an index into a shared tuple records array. */ /* UnsizedArrayOf<F2DOT14> peakTuple - optional */ @@ -515,7 +547,7 @@ struct tuple_delta_t /* pointdata length = 0 implies "use shared points" */ if (points_data_length) - flag |= TupleVariationHeader::TuppleIndex::PrivatePointNumbers; + flag |= TupleVariationHeader::TupleIndex::PrivatePointNumbers; unsigned serialized_data_size = points_data_length + compiled_deltas.length; TupleVariationHeader *o = reinterpret_cast<TupleVariationHeader *> (compiled_tuple_header.begin ()); @@ -531,7 +563,7 @@ struct tuple_delta_t unsigned& flag) const { hb_memcpy (&peak_coords[0], &compiled_peak_coords[0], compiled_peak_coords.length * sizeof (compiled_peak_coords[0])); - flag |= TupleVariationHeader::TuppleIndex::EmbeddedPeakTuple; + flag |= TupleVariationHeader::TupleIndex::EmbeddedPeakTuple; return compiled_peak_coords.length; } @@ -542,7 +574,7 @@ struct tuple_delta_t if (compiled_interm_coords) { hb_memcpy (&coords[0], &compiled_interm_coords[0], compiled_interm_coords.length * sizeof (compiled_interm_coords[0])); - flag |= TupleVariationHeader::TuppleIndex::IntermediateRegion; + flag |= TupleVariationHeader::TupleIndex::IntermediateRegion; } return compiled_interm_coords.length; } @@ -895,15 +927,15 @@ struct TupleVariationData return_trace (c->check_struct (this)); } - unsigned get_size (unsigned axis_count) const + unsigned get_size (unsigned axis_count_times_2) const { unsigned total_size = min_size; unsigned count = tupleVarCount.get_count (); const TupleVariationHeader *tuple_var_header = &(get_tuple_var_header()); for (unsigned i = 0; i < count; i++) { - total_size += tuple_var_header->get_size (axis_count) + tuple_var_header->get_data_size (); - tuple_var_header = &tuple_var_header->get_next (axis_count); + total_size += tuple_var_header->get_size (axis_count_times_2) + tuple_var_header->get_data_size (); + tuple_var_header = &tuple_var_header->get_next (axis_count_times_2); } return total_size; @@ -1398,6 +1430,7 @@ struct TupleVariationData var_data = var_data_bytes_.as<TupleVariationData> (); tuples_left = var_data->tupleVarCount.get_count (); axis_count = axis_count_; + axis_count_times_2 = axis_count_ * 2; current_tuple = &var_data->get_tuple_var_header (); data_offset = 0; table_base = table_base_; @@ -1421,11 +1454,11 @@ struct TupleVariationData return false; current_tuple_size = TupleVariationHeader::min_size; - if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size))) + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) return false; - current_tuple_size = current_tuple->get_size (axis_count); - if (unlikely (!var_data_bytes.check_range (current_tuple, current_tuple_size))) + current_tuple_size = current_tuple->get_size (axis_count_times_2); + if (unlikely (!var_data_bytes.check_end ((const char *) current_tuple + current_tuple_size))) return false; return true; @@ -1448,6 +1481,7 @@ struct TupleVariationData signed tuples_left; const TupleVariationData *var_data; unsigned int axis_count; + unsigned int axis_count_times_2; unsigned int data_offset; unsigned int current_tuple_size; const void *table_base; @@ -1523,6 +1557,7 @@ struct TupleVariationData } template <typename T> + HB_ALWAYS_INLINE static bool decompile_deltas (const HBUINT8 *&p /* IN/OUT */, hb_vector_t<T> &deltas /* IN/OUT */, const HBUINT8 *end, diff --git a/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh b/gfx/harfbuzz/src/hb-ot-var-cvar-table.hh @@ -84,7 +84,7 @@ struct cvar if (!coords) return true; hb_vector_t<unsigned> shared_indices; TupleVariationData<>::tuple_iterator_t iterator; - unsigned var_data_length = tuple_var_data->get_size (axis_count); + unsigned var_data_length = tuple_var_data->get_size (axis_count * 2); hb_bytes_t var_data_bytes = hb_bytes_t (reinterpret_cast<const char*> (tuple_var_data), var_data_length); if (!TupleVariationData<>::get_tuple_iterator (var_data_bytes, axis_count, base, shared_indices, &iterator)) diff --git a/gfx/harfbuzz/src/hb-outline.cc b/gfx/harfbuzz/src/hb-outline.cc @@ -84,6 +84,15 @@ void hb_outline_t::replay (hb_draw_funcs_t *pen, void *pen_data) const } } +void hb_outline_t::translate (float dx, float dy) +{ + for (auto &p : points) + { + p.x += dx; + p.y += dy; + } +} + void hb_outline_t::slant (float slant_xy) { for (auto &p : points) diff --git a/gfx/harfbuzz/src/hb-outline.hh b/gfx/harfbuzz/src/hb-outline.hh @@ -69,6 +69,7 @@ struct hb_outline_t HB_INTERNAL void replay (hb_draw_funcs_t *pen, void *pen_data) const; HB_INTERNAL float control_area () const; + HB_INTERNAL void translate (float dx, float dy); HB_INTERNAL void slant (float slant_xy); HB_INTERNAL void embolden (float x_strength, float y_strength, float x_shift, float y_shift); diff --git a/gfx/harfbuzz/src/hb-sanitize.hh b/gfx/harfbuzz/src/hb-sanitize.hh @@ -64,9 +64,6 @@ * * - Cast blob content to T*, call sanitize() method of it, * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. * - Return empty blob otherwise. * * @@ -98,6 +95,12 @@ * structure is so complicated that by checking all offsets at sanitize() time, * we make the code much simpler in other methods, as offsets and referenced * objects do not need to be validated at each use site. + * + * Note: + * Sanitize was named so because it used to try to recover from errors by + * modifying the data to make it valid. This is no longer the case, as it + * could make HarfBuzz hallucinate new rules if there was aliasing in the + * data. However, the name stuck. See: https://behdad.github.io/harfbust/ */ /* This limits sanitizing time on really broken fonts. */ @@ -125,7 +128,7 @@ struct hb_sanitize_context_t : length (0), max_ops (0), max_subtables (0), recursion_depth (0), - writable (false), edit_count (0), + writable (false), blob (nullptr), num_glyphs (65536), num_glyphs_set (false), @@ -236,7 +239,6 @@ struct hb_sanitize_context_t : this->max_ops = hb_clamp (m, (unsigned) HB_SANITIZE_MAX_OPS_MIN, (unsigned) HB_SANITIZE_MAX_OPS_MAX); - this->edit_count = 0; this->debug_depth = 0; this->recursion_depth = 0; @@ -249,8 +251,8 @@ struct hb_sanitize_context_t : void end_processing () { DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); + "end [%p..%p]", + this->start, this->end); hb_blob_destroy (this->blob); this->blob = nullptr; @@ -258,9 +260,6 @@ struct hb_sanitize_context_t : this->length = 0; } - unsigned get_edit_count () { return edit_count; } - - bool check_ops(unsigned count) { /* Avoid underflow */ @@ -404,35 +403,6 @@ struct hb_sanitize_context_t : return likely (this->check_point ((const char *) obj + obj->min_size)); } - bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%u bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template <typename Type, typename ValueType> - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - * const_cast<Type *> (obj) = v; - return true; - } - return false; - } - template <typename Type> hb_blob_t *sanitize_blob (hb_blob_t *blob) { @@ -440,7 +410,6 @@ struct hb_sanitize_context_t : init (blob); - retry: DEBUG_MSG_FUNC (SANITIZE, start, "start"); start_processing (); @@ -454,38 +423,6 @@ struct hb_sanitize_context_t : Type *t = reinterpret_cast<Type *> (const_cast<char *> (start)); sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %u edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %u edits in second round; FAILING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) - { - unsigned length; - start = hb_blob_get_data_writable (blob, &length); - end = start + length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } end_processing (); @@ -516,7 +453,6 @@ struct hb_sanitize_context_t : private: int recursion_depth; bool writable; - unsigned int edit_count; hb_blob_t *blob; unsigned int num_glyphs; bool num_glyphs_set; diff --git a/gfx/harfbuzz/src/hb-subset-plan-var.cc b/gfx/harfbuzz/src/hb-subset-plan-var.cc @@ -160,36 +160,43 @@ #endif -void +bool normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) { if (plan->user_axes_location.is_empty ()) - return; + return true; hb_array_t<const OT::AxisRecord> axes = face->table.fvar->get_axes (); - plan->normalized_coords.resize (axes.length); + if (!plan->check_success (plan->normalized_coords.resize (axes.length))) + return false; bool has_avar = face->table.avar->has_data (); - const OT::SegmentMaps *seg_maps = nullptr; - unsigned avar_axis_count = 0; + hb_vector_t<float> normalized_mins; + hb_vector_t<float> normalized_defaults; + hb_vector_t<float> normalized_maxs; if (has_avar) { - seg_maps = face->table.avar->get_segment_maps (); - avar_axis_count = face->table.avar->get_axis_count(); + if (!plan->check_success (normalized_mins.resize (axes.length)) || + !plan->check_success (normalized_defaults.resize (axes.length)) || + !plan->check_success (normalized_maxs.resize (axes.length))) + return false; } bool axis_not_pinned = false; - unsigned old_axis_idx = 0, new_axis_idx = 0; - for (const auto& axis : axes) + unsigned new_axis_idx = 0; + unsigned last_idx = 0; + for (const auto& _ : + hb_enumerate (axes)) { + unsigned i = _.first; + const OT::AxisRecord &axis = _.second; hb_tag_t axis_tag = axis.get_axis_tag (); - plan->axes_old_index_tag_map.set (old_axis_idx, axis_tag); + plan->axes_old_index_tag_map.set (i, axis_tag); if (!plan->user_axes_location.has (axis_tag) || !plan->user_axes_location.get (axis_tag).is_point ()) { axis_not_pinned = true; - plan->axes_index_map.set (old_axis_idx, new_axis_idx); + plan->axes_index_map.set (i, new_axis_idx); plan->axis_tags.push (axis_tag); new_axis_idx++; } @@ -216,35 +223,66 @@ normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan) normalized_default = roundf (normalized_default * 16384.f) / 16384.f; normalized_max = roundf (normalized_max * 16384.f) / 16384.f; - if (has_avar && old_axis_idx < avar_axis_count) + if (has_avar) { - normalized_min = seg_maps->map_float (normalized_min); - normalized_default = seg_maps->map_float (normalized_default); - normalized_max = seg_maps->map_float (normalized_max); - - // Round to 2.14 - normalized_min = roundf (normalized_min * 16384.f) / 16384.f; - normalized_default = roundf (normalized_default * 16384.f) / 16384.f; - normalized_max = roundf (normalized_max * 16384.f) / 16384.f; + normalized_mins[i] = normalized_min; + normalized_defaults[i] = normalized_default; + normalized_maxs[i] = normalized_max; + last_idx = i; } - plan->axes_location.set (axis_tag, Triple ((double) normalized_min, - (double) normalized_default, - (double) normalized_max)); - - if (normalized_default == -0.f) - normalized_default = 0.f; // Normalize -0 to 0 - if (normalized_default != 0.f) - plan->pinned_at_default = false; + else + { + plan->axes_location.set (axis_tag, Triple ((double) normalized_min, + (double) normalized_default, + (double) normalized_max)); + if (normalized_default == -0.f) + normalized_default = 0.f; // Normalize -0 to 0 + if (normalized_default != 0.f) + plan->pinned_at_default = false; + + plan->normalized_coords[i] = roundf (normalized_default * 16384.f); + } + } + } + plan->all_axes_pinned = !axis_not_pinned; - plan->normalized_coords[old_axis_idx] = roundf (normalized_default * 16384.f); + // TODO: use avar map_coords_16_16() when normalization is changed to 16.16 + // in fonttools + if (has_avar) + { + const OT::avar* avar_table = face->table.avar; + if (avar_table->has_v2_data () && !plan->all_axes_pinned) + { + DEBUG_MSG (SUBSET, nullptr, "Partial-instancing avar2 table is not supported."); + return false; } - old_axis_idx++; + unsigned coords_len = last_idx + 1; + if (!plan->check_success (avar_table->map_coords_2_14 (normalized_mins.arrayZ, coords_len)) || + !plan->check_success (avar_table->map_coords_2_14 (normalized_defaults.arrayZ, coords_len)) || + !plan->check_success (avar_table->map_coords_2_14 (normalized_maxs.arrayZ, coords_len))) + return false; - if (has_avar && old_axis_idx < avar_axis_count) - seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps); + for (const auto& _ : + hb_enumerate (axes)) + { + unsigned i = _.first; + hb_tag_t axis_tag = _.second.get_axis_tag (); + if (plan->user_axes_location.has (axis_tag)) + { + plan->axes_location.set (axis_tag, Triple ((double) normalized_mins[i], + (double) normalized_defaults[i], + (double) normalized_maxs[i])); + float normalized_default = normalized_defaults[i]; + if (normalized_default == -0.f) + normalized_default = 0.f; // Normalize -0 to 0 + if (normalized_default != 0.f) + plan->pinned_at_default = false; + + plan->normalized_coords[i] = roundf (normalized_default * 16384.f); + } + } } - plan->all_axes_pinned = !axis_not_pinned; + return true; } void diff --git a/gfx/harfbuzz/src/hb-subset-plan.cc b/gfx/harfbuzz/src/hb-subset-plan.cc @@ -677,7 +677,8 @@ hb_subset_plan_t::hb_subset_plan_t (hb_face_t *face, return; #ifndef HB_NO_VAR - normalize_axes_location (face, this); + if (!check_success (normalize_axes_location (face, this))) + return; #endif _populate_unicodes_to_retain (input->sets.unicodes, input->sets.glyphs, this); diff --git a/gfx/harfbuzz/src/hb-subset-plan.hh b/gfx/harfbuzz/src/hb-subset-plan.hh @@ -333,7 +333,7 @@ generate_varstore_inner_maps (const hb_set_t& varidx_set, unsigned subtable_count, hb_vector_t<hb_inc_bimap_t> &inner_maps /* OUT */); -HB_INTERNAL void +HB_INTERNAL bool normalize_axes_location (hb_face_t *face, hb_subset_plan_t *plan); HB_INTERNAL void diff --git a/gfx/harfbuzz/src/hb-vector.hh b/gfx/harfbuzz/src/hb-vector.hh @@ -313,6 +313,11 @@ struct hb_vector_t assert (allocated < 0); allocated = -(allocated + 1); } + void ensure_error () + { + if (!in_error ()) + set_error (); + } Type * _realloc (unsigned new_allocated) diff --git a/gfx/harfbuzz/src/hb-version.h b/gfx/harfbuzz/src/hb-version.h @@ -47,7 +47,7 @@ HB_BEGIN_DECLS * * The minor component of the library version available at compile-time. */ -#define HB_VERSION_MINOR 2 +#define HB_VERSION_MINOR 3 /** * HB_VERSION_MICRO: * @@ -60,7 +60,7 @@ HB_BEGIN_DECLS * * A string literal containing the library version available at compile-time. */ -#define HB_VERSION_STRING "12.2.0" +#define HB_VERSION_STRING "12.3.0" /** * HB_VERSION_ATLEAST: diff --git a/gfx/harfbuzz/src/rust/Cargo.toml b/gfx/harfbuzz/src/rust/Cargo.toml @@ -5,7 +5,7 @@ rust-version = "1.87.0" [dependencies] skrifa = { version = "0.*", optional = true } -harfrust = { version = "0.3.0", optional = true } +harfrust = { version = "0.4.*", optional = true } # harfrust = { git = "https://github.com/harfbuzz/harfrust", optional = true } [lib]