Update HarfBuzz to 12.3.2

This commit is contained in:
DeeJayLSP 2026-01-20 22:49:45 -03:00
parent 78c6632eb1
commit 7da2925a95
63 changed files with 1106 additions and 876 deletions

View file

@ -451,7 +451,7 @@ Patches:
## harfbuzz
- Upstream: https://github.com/harfbuzz/harfbuzz
- Version: 12.2.0 (0426759f2b03e479495e987608f10e0866e70180, 2025)
- Version: 12.3.2 (b42511e071162fe76102f613a6ccc009726c99af, 2026)
- License: MIT
Files extracted from upstream source:

View file

@ -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;

View file

@ -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;

View file

@ -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);
};
@ -1002,20 +1002,34 @@ struct GDEF
}
HB_ALWAYS_INLINE
bool mark_set_covers (unsigned int set_index, hb_codepoint_t glyph_id) const
{
return
#ifndef HB_NO_GDEF_CACHE
mark_glyph_sets[set_index].may_have (glyph_id)
#else
table->mark_set_covers (set_index, glyph_id)
// We can access arrayZ directly because of sanitize_lookup_props() guarantee.
mark_glyph_sets.arrayZ[set_index].may_have (glyph_id) &&
#endif
table->mark_set_covers (set_index, glyph_id)
;
}
unsigned sanitize_lookup_props (unsigned lookup_props) const
{
#ifndef HB_NO_GDEF_CACHE
if (lookup_props & LookupFlag::UseMarkFilteringSet &&
(lookup_props >> 16) >= mark_glyph_sets.length)
{
// Invalid mark filtering set index; unset the flag.
lookup_props &= ~LookupFlag::UseMarkFilteringSet;
}
#endif
return lookup_props;
}
hb_blob_ptr_t<GDEF> table;
#ifndef HB_NO_GDEF_CACHE
hb_vector_t<hb_bit_set_t> mark_glyph_sets;
hb_vector_t<hb_set_digest_t> mark_glyph_sets;
mutable hb_cache_t<21, 3> glyph_props_cache;
static_assert (sizeof (glyph_props_cache) == 512, "");
#endif

View file

@ -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:

View file

@ -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 ());
}

View file

@ -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();

View file

@ -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)...));

View file

@ -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)...));

View file

@ -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)...));

View file

@ -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

View file

@ -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 ());

View file

@ -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);
}

View file

@ -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);

View file

@ -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,

View file

@ -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);

View file

@ -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);
}

View file

@ -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 ());
}

View file

@ -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),

View file

@ -29,8 +29,11 @@
#ifndef OT_LAYOUT_TYPES_HH
#define OT_LAYOUT_TYPES_HH
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_mapping_cache_t = hb_cache_t<16, 8, 8>;
static_assert (sizeof (hb_ot_layout_mapping_cache_t) == 512, "");
using hb_ot_layout_binary_cache_t = hb_cache_t<14, 1, 8>;
static_assert (sizeof (hb_ot_layout_binary_cache_t) == 256, "");
namespace OT {
namespace Layout {

View file

@ -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);

View file

@ -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);

View file

@ -1097,24 +1097,6 @@ struct graph_t
return clone_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, 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
@ -1258,6 +1240,34 @@ struct graph_t
return clone_idx;
}
/*
* 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.
*/

View file

@ -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_);

View file

@ -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 (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 (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;
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,17 +279,15 @@ 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;
unsigned shift = 0;
for (const auto& p : subtable_ids)
{
unsigned insert_index = p.first + shift;
unsigned pos_offset = p.second.length * OT::Offset16::static_size;
unsigned insert_offset = (char*) &lookup->subTable[insert_index] - (char*) lookup;
unsigned insert_offset = Lookup::min_size + insert_index * OT::Offset16::static_size;
shift += p.second.length;
for (auto& l : v.obj.all_links_writer ())
@ -326,7 +331,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);
}

View file

@ -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,36 @@ 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];
unsigned coverage_size = coverage_v.table_size ();
Coverage* coverage_table = (Coverage*) coverage_v.obj.head;
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 ();
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 +476,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 +493,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

View file

@ -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

View file

@ -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:

View file

@ -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;

View file

@ -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.
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);
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);
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 ();
}

View file

@ -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;
{

View file

@ -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];
};

View file

@ -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; }

View file

@ -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;
};

View file

@ -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), "");

View file

@ -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 */
/**

View file

@ -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

View file

@ -37,48 +37,6 @@
#endif
/*
* 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.)
*/

View file

@ -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)
{

View file

@ -24,22 +24,140 @@
#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
#pragma GCC diagnostic ignored "-Wcast-align"
#pragma GCC diagnostic ignored "-Wunused-function"
#pragma GCC diagnostic ignored "-Wsign-compare"
#pragma GCC diagnostic ignored "-Wextra-semi-stmt"
#include "kb_text_shape.h"
#pragma GCC diagnostic pop
static hb_user_data_key_t hb_kbts_shape_plan_data_key = {0};
struct hb_kbts_shape_plan_data_t
{
kbts_shape_config *config = nullptr;
mutable hb_atomic_t<kbts_shape_scratchpad *> scratchpad;
};
static void
hb_kbts_shape_plan_data_destroy (void *data)
{
auto *plan_data = (hb_kbts_shape_plan_data_t *) data;
if (!plan_data)
return;
auto *scratchpad = plan_data->scratchpad.get_acquire ();
if (scratchpad && plan_data->scratchpad.cmpexch (scratchpad, nullptr))
kbts_DestroyShapeScratchpad (scratchpad);
if (plan_data->config)
kbts_DestroyShapeConfig (plan_data->config);
hb_free (plan_data);
}
static void
hb_kbts_plan_props_to_script_language (const hb_segment_properties_t &props,
kbts_script *kb_script,
kbts_language *kb_language)
{
hb_tag_t scripts[HB_OT_MAX_TAGS_PER_SCRIPT];
hb_tag_t language;
unsigned int script_count = ARRAY_LENGTH (scripts);
unsigned int language_count = 1;
*kb_script = KBTS_SCRIPT_DONT_KNOW;
*kb_language = KBTS_LANGUAGE_DEFAULT;
hb_ot_tags_from_script_and_language (props.script, props.language,
&script_count, scripts,
&language_count, &language);
for (unsigned int i = 0; i < script_count && scripts[i] != HB_TAG_NONE; ++i)
{
*kb_script = kbts_ScriptTagToScript (hb_uint32_swap (scripts[i]));
if (*kb_script != KBTS_SCRIPT_DONT_KNOW)
break;
}
if (language_count)
*kb_language = (kbts_language) hb_uint32_swap (language);
}
static hb_kbts_shape_plan_data_t *
hb_kbts_get_shape_plan_data (hb_shape_plan_t *shape_plan,
kbts_font *kb_font)
{
retry:
auto *plan_data = (hb_kbts_shape_plan_data_t *)
hb_shape_plan_get_user_data (shape_plan, &hb_kbts_shape_plan_data_key);
if (plan_data)
return plan_data;
kbts_script kb_script;
kbts_language kb_language;
hb_kbts_plan_props_to_script_language (shape_plan->key.props, &kb_script, &kb_language);
kbts_shape_config *config = kbts_CreateShapeConfig (kb_font, kb_script, kb_language, nullptr, nullptr);
if (unlikely (!config))
return nullptr;
plan_data = (hb_kbts_shape_plan_data_t *) hb_calloc (1, sizeof (*plan_data));
if (unlikely (!plan_data))
{
kbts_DestroyShapeConfig (config);
return nullptr;
}
plan_data->config = config;
plan_data->scratchpad.init (nullptr);
if (!hb_shape_plan_set_user_data (shape_plan,
&hb_kbts_shape_plan_data_key,
plan_data,
hb_kbts_shape_plan_data_destroy,
false))
{
hb_kbts_shape_plan_data_destroy (plan_data);
goto retry;
}
return plan_data;
}
static kbts_shape_scratchpad *
hb_kbts_acquire_shape_scratchpad (hb_kbts_shape_plan_data_t *plan_data)
{
auto *scratchpad = plan_data->scratchpad.get_acquire ();
if (!scratchpad || unlikely (!plan_data->scratchpad.cmpexch (scratchpad, nullptr)))
scratchpad = kbts_CreateShapeScratchpad (plan_data->config, nullptr, nullptr);
return scratchpad;
}
static void
hb_kbts_release_shape_scratchpad (hb_kbts_shape_plan_data_t *plan_data,
kbts_shape_scratchpad *scratchpad)
{
if (!scratchpad)
return;
if (!plan_data->scratchpad.cmpexch (nullptr, scratchpad))
kbts_DestroyShapeScratchpad (scratchpad);
}
hb_kbts_face_data_t *
_hb_kbts_shaper_face_data_create (hb_face_t *face)
@ -55,40 +173,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 +202,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,116 +242,84 @@ _hb_kbts_shape (hb_shape_plan_t *shape_plan,
return false;
}
kbts_script kb_script = KBTS_SCRIPT_DONT_KNOW;
kbts_language kb_language = KBTS_LANGUAGE_DEFAULT;
{
hb_tag_t scripts[HB_OT_MAX_TAGS_PER_SCRIPT];
hb_tag_t language;
unsigned int script_count = ARRAY_LENGTH (scripts);
unsigned int language_count = 1;
hb_ot_tags_from_script_and_language (buffer->props.script, buffer->props.language,
&script_count, scripts,
&language_count, &language);
for (unsigned int i = 0; i < script_count && scripts[i] != HB_TAG_NONE; ++i)
{
kb_script = kbts_ScriptTagToScript (hb_uint32_swap (scripts[i]));
if (kb_script != KBTS_SCRIPT_DONT_KNOW)
break;
}
if (language_count)
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)))
kbts_glyph_storage kb_glyph_storage;
if (unlikely (!kbts_InitializeGlyphStorage (&kb_glyph_storage, nullptr, nullptr)))
return false;
hb_kbts_shape_plan_data_t *plan_data = hb_kbts_get_shape_plan_data (shape_plan, kb_font);
if (unlikely (!plan_data))
{
kbts_FreeAllGlyphs (&kb_glyph_storage);
return false;
}
kbts_shape_config *kb_shape_config = nullptr;
kbts_shape_scratchpad *kb_shape_scratchpad = nullptr;
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;
hb_bool_t res = false;
kb_shape_config = plan_data->config;
kb_shape_scratchpad = hb_kbts_acquire_shape_scratchpad (plan_data);
if (unlikely (!kb_shape_scratchpad))
goto done;
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_shape_config, 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;
}
res = kbts_ShapeDirect (kb_shape_scratchpad, &kb_glyph_storage, kb_direction, &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)
{
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 = buffer->info;
pos = buffer->pos;
for (auto it = kb_output; kbts_GlyphIteratorNext (&it, &kb_glyph); info++, pos++)
{
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:
if (likely (kb_shape_scratchpad))
hb_kbts_release_shape_scratchpad (plan_data, kb_shape_scratchpad);
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
#endif

View file

@ -492,16 +492,16 @@ struct hb_hashmap_t
hb_hashmap_t& operator << (const hb_pair_t<K, V>& v)
{ set (v.first, v.second); return *this; }
template <typename V2 = V,
hb_enable_if (!std::is_trivially_copyable<V2>::value)>
hb_enable_if (!hb_is_trivially_copyable (V2))>
hb_hashmap_t& operator << (const hb_pair_t<K, V&&>& v)
{ set (v.first, std::move (v.second)); return *this; }
template <typename K2 = K,
hb_enable_if (!std::is_trivially_copyable<K2>::value)>
hb_enable_if (!hb_is_trivially_copyable (K2))>
hb_hashmap_t& operator << (const hb_pair_t<K&&, V>& v)
{ set (std::move (v.first), v.second); return *this; }
template <typename K2 = K, typename V2 = V,
hb_enable_if (!std::is_trivially_copyable<K2>::value &&
!std::is_trivially_copyable<V2>::value)>
hb_enable_if (!hb_is_trivially_copyable (K2) &&
!hb_is_trivially_copyable (V2))>
hb_hashmap_t& operator << (const hb_pair_t<K&&, V&&>& v)
{ set (std::move (v.first), std::move (v.second)); return *this; }

View file

@ -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);
};

View file

@ -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

View file

@ -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);
};
@ -1679,6 +1670,10 @@ struct SubtableUnicodesCache {
{
SubtableUnicodesCache* cache =
(SubtableUnicodesCache*) hb_malloc (sizeof(SubtableUnicodesCache));
if (unlikely (!cache))
return nullptr;
new (cache) SubtableUnicodesCache (source_table);
return cache;
}
@ -1772,6 +1767,10 @@ struct cmap
;
SubtableUnicodesCache* cache = SubtableUnicodesCache::create(source_table);
if (unlikely (!cache))
return nullptr;
for (const EncodingRecord& _ : it)
cache->set_for(&_); // populate the cache for this encoding record.
@ -1806,7 +1805,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 +1907,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 +1932,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 +2020,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 +2064,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 +2271,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:

View file

@ -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 {

View file

@ -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

View file

@ -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>

View file

@ -582,11 +582,12 @@ struct skipping_iterator_t
#endif
bool next (unsigned *unsafe_to = nullptr)
{
auto *info = c->buffer->info;
const signed stop = (signed) end - 1;
while ((signed) idx < stop)
{
idx++;
switch (match (c->buffer->info[idx]))
switch (match (info[idx]))
{
case MATCH:
{
@ -612,11 +613,12 @@ struct skipping_iterator_t
#endif
bool prev (unsigned *unsafe_from = nullptr)
{
auto *out_info = c->buffer->out_info;
const unsigned stop = 0;
while (idx > stop)
{
idx--;
switch (match (c->buffer->out_info[idx]))
switch (match (out_info[idx]))
{
case MATCH:
{
@ -786,7 +788,11 @@ struct hb_ot_apply_context_t :
void set_random (bool random_) { random = random_; }
void set_recurse_func (recurse_func_t func) { recurse_func = func; }
void set_lookup_index (unsigned int lookup_index_) { lookup_index = lookup_index_; }
void set_lookup_props (unsigned int lookup_props_) { lookup_props = lookup_props_; init_iters (); }
void set_lookup_props (unsigned int lookup_props_)
{
lookup_props = gdef_accel.sanitize_lookup_props (lookup_props_);
init_iters ();
}
uint32_t random_number ()
{
@ -831,7 +837,7 @@ struct hb_ot_apply_context_t :
if (glyph_props & match_props & LookupFlag::IgnoreFlags)
return false;
if (unlikely (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK))
if (glyph_props & HB_OT_LAYOUT_GLYPH_PROPS_MARK)
return match_properties_mark (info, glyph_props, match_props);
return true;
@ -1312,10 +1318,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 +1575,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 +1614,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);
@ -1973,13 +2000,12 @@ static inline bool context_would_apply_lookup (hb_would_apply_context_t *c,
}
template <typename HBUINT>
HB_ALWAYS_INLINE
static bool context_apply_lookup (hb_ot_apply_context_t *c,
unsigned int inputCount, /* Including the first glyph (not matched) */
const HBUINT input[], /* Array of input values--start with second glyph */
unsigned int lookupCount,
const LookupRecord lookupRecord[],
const ContextApplyLookupContext &lookup_context)
static inline bool context_apply_lookup (hb_ot_apply_context_t *c,
unsigned int inputCount, /* Including the first glyph (not matched) */
const HBUINT input[], /* Array of input values--start with second glyph */
unsigned int lookupCount,
const LookupRecord lookupRecord[],
const ContextApplyLookupContext &lookup_context)
{
if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false;
@ -2246,24 +2272,29 @@ 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);
unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0;
unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0;
hb_glyph_info_t *first = nullptr, *second = nullptr;
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_to1 = skippy_iter.idx + 1;
}
else
{
@ -2279,8 +2310,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 +2355,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 +2713,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 +2991,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 +3007,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;
@ -3076,17 +3141,16 @@ static inline bool chain_context_would_apply_lookup (hb_would_apply_context_t *c
}
template <typename HBUINT>
HB_ALWAYS_INLINE
static bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
unsigned int backtrackCount,
const HBUINT backtrack[],
unsigned int inputCount, /* Including the first glyph (not matched) */
const HBUINT input[], /* Array of input values--start with second glyph */
unsigned int lookaheadCount,
const HBUINT lookahead[],
unsigned int lookupCount,
const LookupRecord lookupRecord[],
const ChainContextApplyLookupContext &lookup_context)
static inline bool chain_context_apply_lookup (hb_ot_apply_context_t *c,
unsigned int backtrackCount,
const HBUINT backtrack[],
unsigned int inputCount, /* Including the first glyph (not matched) */
const HBUINT input[], /* Array of input values--start with second glyph */
unsigned int lookaheadCount,
const HBUINT lookahead[],
unsigned int lookupCount,
const LookupRecord lookupRecord[],
const ChainContextApplyLookupContext &lookup_context)
{
if (unlikely (inputCount > HB_MAX_CONTEXT_LENGTH)) return false;
@ -3404,33 +3468,29 @@ 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);
unsigned unsafe_to = (unsigned) -1, unsafe_to1 = 0, unsafe_to2 = 0;
unsigned unsafe_to = (unsigned) -1, unsafe_to1, unsafe_to2 = 0;
hb_glyph_info_t *first = nullptr, *second = nullptr;
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 +3511,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 +3566,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 +3953,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 +4324,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 +4340,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 +4415,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 +4423,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 +4435,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 +4444,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 +4454,7 @@ struct Extension
protected:
union {
HBUINT16 format; /* Format identifier */
struct { HBUINT16 v; } format; /* Format identifier */
ExtensionFormat1<T> format1;
} u;
};

View file

@ -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

View file

@ -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'));
}

View file

@ -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>>

View file

@ -411,6 +411,56 @@ 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;
#else
return version.major < 2;
#endif
}
bool subset (hb_subset_context_t *c) const
{
TRACE_SUBSET (this);

View file

@ -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 */
@ -288,13 +320,13 @@ struct tuple_delta_t
void copy_from (const tuple_delta_t& o, hb_alloc_pool_t *pool = nullptr)
{
axis_tuples = o.axis_tuples;
indices.allocate_from_pool (pool, o.indices);
deltas_x.allocate_from_pool (pool, o.deltas_x);
deltas_y.allocate_from_pool (pool, o.deltas_y);
compiled_tuple_header.allocate_from_pool (pool, o.compiled_tuple_header);
compiled_deltas.allocate_from_pool (pool, o.compiled_deltas);
compiled_peak_coords.allocate_from_pool (pool, o.compiled_peak_coords);
compiled_interm_coords.allocate_from_pool (pool, o.compiled_interm_coords);
indices.duplicate_vector_from_pool (pool, o.indices);
deltas_x.duplicate_vector_from_pool (pool, o.deltas_x);
deltas_y.duplicate_vector_from_pool (pool, o.deltas_y);
compiled_tuple_header.duplicate_vector_from_pool (pool, o.compiled_tuple_header);
compiled_deltas.duplicate_vector_from_pool (pool, o.compiled_deltas);
compiled_peak_coords.duplicate_vector_from_pool (pool, o.compiled_peak_coords);
compiled_interm_coords.duplicate_vector_from_pool (pool, o.compiled_interm_coords);
}
void remove_axis (hb_tag_t axis_tag)
@ -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,

View file

@ -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))

View file

@ -448,10 +448,18 @@ struct gvar_GVAR
if (it->first == 0 && !(c->plan->flags & HB_SUBSET_FLAGS_NOTDEF_OUTLINE))
it++;
unsigned int subset_data_size = 0;
unsigned padding_size = 0;
for (auto &_ : it)
{
hb_codepoint_t old_gid = _.second;
subset_data_size += get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length;
unsigned glyph_data_size = get_glyph_var_data_bytes (c->source_blob, glyph_count, old_gid).length;
if (glyph_data_size % 2)
{
glyph_data_size++;
padding_size++;
}
subset_data_size += glyph_data_size;
}
/* According to the spec: If the short format (Offset16) is used for offsets,
@ -480,6 +488,8 @@ struct gvar_GVAR
/* This ordering relative to the shared tuples array, which puts the glyphVariationData
last in the table, is required when HB_SUBSET_FLAGS_IFTB_REQUIREMENTS is set */
if (long_offset)
subset_data_size -= padding_size;
char *subset_data = c->serializer->allocate_size<char> (subset_data_size, false);
if (!subset_data) return_trace (false);
out->dataZ = subset_data - (char *) out;
@ -518,8 +528,16 @@ struct gvar_GVAR
old_gid);
hb_memcpy (subset_data, var_data_bytes.arrayZ, var_data_bytes.length);
subset_data += var_data_bytes.length;
glyph_offset += var_data_bytes.length;
unsigned glyph_data_size = var_data_bytes.length;
subset_data += glyph_data_size;
glyph_offset += glyph_data_size;
if (!long_offset && (glyph_data_size % 2))
{
*subset_data = 0;
subset_data++;
glyph_offset++;
}
if (long_offset)
((HBUINT32 *) subset_offsets)[gid] = glyph_offset;

View file

@ -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)

View file

@ -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);

View file

@ -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;

View file

@ -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));
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;
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[old_axis_idx] = roundf (normalized_default * 16384.f);
plan->normalized_coords[i] = roundf (normalized_default * 16384.f);
}
}
old_axis_idx++;
if (has_avar && old_axis_idx < avar_axis_count)
seg_maps = &StructAfter<OT::SegmentMaps> (*seg_maps);
}
plan->all_axes_pinned = !axis_not_pinned;
// 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;
}
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;
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);
}
}
}
return true;
}
void

View file

@ -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);

View file

@ -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

View file

@ -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)
@ -565,7 +570,7 @@ struct hb_vector_t
template <typename allocator_t>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool allocate_from_pool (allocator_t *allocator, const hb_vector_t &other)
bool duplicate_vector_from_pool (allocator_t *allocator, const hb_vector_t &other)
{
if (unlikely (!allocate_from_pool (allocator, other.length, false)))
return false;

View file

@ -47,20 +47,20 @@ 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:
*
* The micro component of the library version available at compile-time.
*/
#define HB_VERSION_MICRO 0
#define HB_VERSION_MICRO 2
/**
* HB_VERSION_STRING:
*
* A string literal containing the library version available at compile-time.
*/
#define HB_VERSION_STRING "12.2.0"
#define HB_VERSION_STRING "12.3.2"
/**
* HB_VERSION_ATLEAST: