Update Harfbuzz to version 10.3.0

[ChangeLog][Third-Party Code] Upgraded Harfbuzz to version
10.3.0.

Pick-to: 6.8 6.5 5.15
Change-Id: I3a4daaa9c0747c4f4717ac8f0f9e7baf9a9a449e
Reviewed-by: Eirik Aavitsland <eirik.aavitsland@qt.io>
(cherry picked from commit 82a3357d007837b1d54dcf6dd6309363649d186f)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Eskil Abrahamsen Blomfeldt 2025-02-26 08:35:53 +01:00 committed by Qt Cherry-pick Bot
parent 0777144b45
commit 1a4e2e869f
83 changed files with 2467 additions and 1257 deletions

View File

@ -25,6 +25,7 @@ qt_internal_add_3rdparty_library(BundledHarfbuzz
src/hb-cache.hh src/hb-cache.hh
src/hb-common.h src/hb-common.h
src/hb-debug.hh src/hb-debug.hh
src/hb-decycler.hh
src/hb-deprecated.h src/hb-deprecated.h
src/hb-draw.cc src/hb-draw.h src/hb-draw.hh src/hb-draw.cc src/hb-draw.h src/hb-draw.hh
src/hb-face.cc src/hb-face.h src/hb-face.hh src/hb-face.cc src/hb-face.h src/hb-face.hh

View File

@ -3,7 +3,6 @@
[![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html) [![OSS-Fuzz Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/harfbuzz.svg)](https://oss-fuzz-build-logs.storage.googleapis.com/index.html)
[![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz) [![Coverity Scan Build Status](https://scan.coverity.com/projects/15166/badge.svg)](https://scan.coverity.com/projects/harfbuzz)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://app.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade) [![Codacy Badge](https://app.codacy.com/project/badge/Grade/89c872f5ce1c42af802602bfcd15d90a)](https://app.codacy.com/gh/harfbuzz/harfbuzz/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
[![Codecov Code Coverage](https://codecov.io/gh/harfbuzz/harfbuzz/branch/main/graph/badge.svg)](https://codecov.io/gh/harfbuzz/harfbuzz)
[![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions) [![Packaging status](https://repology.org/badge/tiny-repos/harfbuzz.svg)](https://repology.org/project/harfbuzz/versions)
[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz) [![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/harfbuzz/harfbuzz/badge)](https://securityscorecards.dev/viewer/?uri=github.com/harfbuzz/harfbuzz)

View File

@ -7,8 +7,8 @@
"Description": "HarfBuzz is an OpenType text shaping engine.", "Description": "HarfBuzz is an OpenType text shaping engine.",
"Homepage": "http://harfbuzz.org", "Homepage": "http://harfbuzz.org",
"Version": "10.2.0", "Version": "10.3.0",
"DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/10.2.0", "DownloadLocation": "https://github.com/harfbuzz/harfbuzz/releases/tag/10.3.0",
"PURL": "pkg:github/harfbuzz/harfbuzz@$<VERSION>", "PURL": "pkg:github/harfbuzz/harfbuzz@$<VERSION>",
"CPE": "cpe:2.3:a:harfbuzz_project:harfbuzz:$<VERSION>:*:*:*:*:*:*:*", "CPE": "cpe:2.3:a:harfbuzz_project:harfbuzz:$<VERSION>:*:*:*:*:*:*:*",
"License": "MIT License", "License": "MIT License",

View File

@ -29,11 +29,14 @@
#define OT_COLOR_COLR_COLR_HH #define OT_COLOR_COLR_COLR_HH
#include "../../../hb.hh" #include "../../../hb.hh"
#include "../../../hb-decycler.hh"
#include "../../../hb-open-type.hh" #include "../../../hb-open-type.hh"
#include "../../../hb-ot-var-common.hh" #include "../../../hb-ot-var-common.hh"
#include "../../../hb-paint.hh" #include "../../../hb-paint.hh"
#include "../../../hb-paint-extents.hh" #include "../../../hb-paint-extents.hh"
#include "../CPAL/CPAL.hh"
/* /*
* COLR -- Color * COLR -- Color
* https://docs.microsoft.com/en-us/typography/opentype/spec/colr * https://docs.microsoft.com/en-us/typography/opentype/spec/colr
@ -66,11 +69,11 @@ public:
hb_paint_funcs_t *funcs; hb_paint_funcs_t *funcs;
void *data; void *data;
hb_font_t *font; hb_font_t *font;
unsigned int palette_index; hb_array_t<const BGRAColor> palette;
hb_color_t foreground; hb_color_t foreground;
ItemVarStoreInstancer &instancer; ItemVarStoreInstancer &instancer;
hb_map_t current_glyphs; hb_decycler_t glyphs_decycler;
hb_map_t current_layers; hb_decycler_t layers_decycler;
int depth_left = HB_MAX_NESTING_LEVEL; int depth_left = HB_MAX_NESTING_LEVEL;
int edge_count = HB_MAX_GRAPH_EDGE_COUNT; int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
@ -85,7 +88,11 @@ public:
funcs (funcs_), funcs (funcs_),
data (data_), data (data_),
font (font_), font (font_),
palette_index (palette_), palette (
#ifndef HB_NO_COLOR
font->face->table.CPAL->get_palette_colors (palette_)
#endif
),
foreground (foreground_), foreground (foreground_),
instancer (instancer_) instancer (instancer_)
{ } { }
@ -99,12 +106,7 @@ public:
if (color_index != 0xffff) if (color_index != 0xffff)
{ {
if (!funcs->custom_palette_color (data, color_index, &color)) if (!funcs->custom_palette_color (data, color_index, &color))
{ color = palette[color_index];
unsigned int clen = 1;
hb_face_t *face = hb_font_get_face (font);
hb_ot_color_palette_get_colors (face, palette_index, color_index, &clen, &color);
}
*is_foreground = false; *is_foreground = false;
} }
@ -2134,12 +2136,16 @@ struct COLR
const ItemVariationStore &get_var_store () const const ItemVariationStore &get_var_store () const
{ return colr->get_var_store (); } { return colr->get_var_store (); }
const ItemVariationStore *get_var_store_ptr () const
{ return colr->get_var_store_ptr (); }
bool has_delta_set_index_map () const bool has_delta_set_index_map () const
{ return colr->has_delta_set_index_map (); } { return colr->has_delta_set_index_map (); }
const DeltaSetIndexMap &get_delta_set_index_map () const const DeltaSetIndexMap &get_delta_set_index_map () const
{ return colr->get_delta_set_index_map (); } { return colr->get_delta_set_index_map (); }
const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
{ return colr->get_delta_set_index_map_ptr (); }
private: private:
hb_blob_ptr_t<COLR> colr; hb_blob_ptr_t<COLR> colr;
@ -2232,9 +2238,13 @@ struct COLR
const DeltaSetIndexMap &get_delta_set_index_map () const const DeltaSetIndexMap &get_delta_set_index_map () const
{ return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); } { return has_delta_set_index_map () && hb_barrier () ? this+varIdxMap : Null (DeltaSetIndexMap); }
const DeltaSetIndexMap *get_delta_set_index_map_ptr () const
{ return has_delta_set_index_map () && hb_barrier () ? &(this+varIdxMap) : nullptr; }
const ItemVariationStore &get_var_store () const const ItemVariationStore &get_var_store () const
{ return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); } { return has_var_store () && hb_barrier () ? this+varStore : Null (ItemVariationStore); }
const ItemVariationStore *get_var_store_ptr () const
{ return has_var_store () && hb_barrier () ? &(this+varStore) : nullptr; }
const ClipList &get_clip_list () const const ClipList &get_clip_list () const
{ return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); } { return has_clip_list () && hb_barrier () ? this+clipList : Null (ClipList); }
@ -2482,8 +2492,8 @@ struct COLR
* after instancing */ * after instancing */
if (!subset_varstore (c, colr_prime)) return_trace (false); if (!subset_varstore (c, colr_prime)) return_trace (false);
ItemVarStoreInstancer instancer (&(get_var_store ()), ItemVarStoreInstancer instancer (get_var_store_ptr (),
&(get_delta_set_index_map ()), get_delta_set_index_map_ptr (),
c->plan->normalized_coords.as_array ()); c->plan->normalized_coords.as_array ());
if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer)) if (!colr_prime->baseGlyphList.serialize_subset (c, baseGlyphList, this, instancer))
@ -2513,8 +2523,8 @@ struct COLR
get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const
{ {
ItemVarStoreInstancer instancer (&(get_var_store ()), ItemVarStoreInstancer instancer (get_var_store_ptr (),
&(get_delta_set_index_map ()), get_delta_set_index_map_ptr (),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
if (get_clip (glyph, extents, instancer)) if (get_clip (glyph, extents, instancer))
@ -2575,11 +2585,13 @@ struct COLR
bool bool
paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, unsigned int palette_index, hb_color_t foreground, bool clip = true) const
{ {
ItemVarStoreInstancer instancer (&(get_var_store ()), ItemVarStoreInstancer instancer (get_var_store_ptr (),
&(get_delta_set_index_map ()), get_delta_set_index_map_ptr (),
hb_array (font->coords, font->num_coords)); hb_array (font->coords, font->num_coords));
hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer); hb_paint_context_t c (this, funcs, data, font, palette_index, foreground, instancer);
c.current_glyphs.add (glyph);
hb_decycler_node_t node (c.glyphs_decycler);
node.visit (glyph);
if (version >= 1) if (version >= 1)
{ {
@ -2695,19 +2707,16 @@ void PaintColrLayers::paint_glyph (hb_paint_context_t *c) const
{ {
TRACE_PAINT (this); TRACE_PAINT (this);
const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList (); const LayerList &paint_offset_lists = c->get_colr_table ()->get_layerList ();
hb_decycler_node_t node (c->layers_decycler);
for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++) for (unsigned i = firstLayerIndex; i < firstLayerIndex + numLayers; i++)
{ {
if (unlikely (c->current_layers.has (i))) if (unlikely (!node.visit (i)))
continue; return;
c->current_layers.add (i);
const Paint &paint = paint_offset_lists.get_paint (i); const Paint &paint = paint_offset_lists.get_paint (i);
c->funcs->push_group (c->data); c->funcs->push_group (c->data);
c->recurse (paint); c->recurse (paint);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
c->current_layers.del (i);
} }
} }
@ -2715,16 +2724,14 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
{ {
TRACE_PAINT (this); TRACE_PAINT (this);
if (unlikely (c->current_glyphs.has (gid))) hb_decycler_node_t node (c->glyphs_decycler);
if (unlikely (!node.visit (gid)))
return; return;
c->current_glyphs.add (gid);
c->funcs->push_inverse_root_transform (c->data, c->font); c->funcs->push_inverse_root_transform (c->data, c->font);
if (c->funcs->color_glyph (c->data, gid, c->font)) if (c->funcs->color_glyph (c->data, gid, c->font))
{ {
c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data);
c->current_glyphs.del (gid);
return; return;
} }
c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data);
@ -2747,8 +2754,6 @@ void PaintColrGlyph::paint_glyph (hb_paint_context_t *c) const
if (has_clip_box) if (has_clip_box)
c->funcs->pop_clip (c->data); c->funcs->pop_clip (c->data);
c->current_glyphs.del (gid);
} }
} /* namespace OT */ } /* namespace OT */

View File

@ -187,6 +187,14 @@ struct CPAL
hb_ot_name_id_t get_color_name_id (unsigned int color_index) const hb_ot_name_id_t get_color_name_id (unsigned int color_index) const
{ return v1 ().get_color_name_id (this, color_index, numColors); } { return v1 ().get_color_name_id (this, color_index, numColors); }
hb_array_t<const BGRAColor> get_palette_colors (unsigned int palette_index) const
{
if (unlikely (palette_index >= numPalettes))
return hb_array_t<const BGRAColor> ();
unsigned int start_index = colorRecordIndicesZ[palette_index];
hb_array_t<const BGRAColor> all_colors ((this+colorRecordsZ).arrayZ, numColorRecords);
return all_colors.sub_array (start_index, numColors);
}
unsigned int get_palette_colors (unsigned int palette_index, unsigned int get_palette_colors (unsigned int palette_index,
unsigned int start_offset, unsigned int start_offset,
unsigned int *color_count, /* IN/OUT. May be NULL. */ unsigned int *color_count, /* IN/OUT. May be NULL. */

View File

@ -96,6 +96,15 @@ struct Coverage
default:return NOT_COVERED; default:return NOT_COVERED;
} }
} }
unsigned int get_coverage (hb_codepoint_t glyph_id,
hb_ot_lookup_cache_t *cache) const
{
unsigned coverage;
if (cache && cache->get (glyph_id, &coverage)) return coverage;
coverage = get_coverage (glyph_id);
if (cache) cache->set (glyph_id, coverage);
return coverage;
}
unsigned get_population () const unsigned get_population () const
{ {
@ -201,6 +210,19 @@ struct Coverage
} }
} }
unsigned cost () const
{
switch (u.format) {
case 1: hb_barrier (); return u.format1.cost ();
case 2: hb_barrier (); return u.format2.cost ();
#ifndef HB_NO_BEYOND_64K
case 3: hb_barrier (); return u.format3.cost ();
case 4: hb_barrier (); return u.format4.cost ();
#endif
default:return 0u;
}
}
/* Might return false if array looks unsorted. /* Might return false if array looks unsorted.
* Used for faster rejection of corrupt data. */ * Used for faster rejection of corrupt data. */
template <typename set_t> template <typename set_t>

View File

@ -103,6 +103,8 @@ struct CoverageFormat1_3
intersect_glyphs << glyphArray[i]; intersect_glyphs << glyphArray[i];
} }
unsigned cost () const { return hb_bit_storage ((unsigned) glyphArray.len); /* bsearch cost */ }
template <typename set_t> template <typename set_t>
bool collect_coverage (set_t *glyphs) const bool collect_coverage (set_t *glyphs) const
{ return glyphs->add_sorted_array (glyphArray.as_array ()); } { return glyphs->add_sorted_array (glyphArray.as_array ()); }

View File

@ -157,6 +157,8 @@ struct CoverageFormat2_4
} }
} }
unsigned cost () const { return hb_bit_storage ((unsigned) rangeRecord.len); /* bsearch cost */ }
template <typename set_t> template <typename set_t>
bool collect_coverage (set_t *glyphs) const bool collect_coverage (set_t *glyphs) const
{ {

View File

@ -103,12 +103,50 @@ struct PairPosFormat1_3
const Coverage &get_coverage () const { return this+coverage; } const Coverage &get_coverage () const { return this+coverage; }
bool apply (hb_ot_apply_context_t *c) const unsigned cache_cost () const
{
return (this+coverage).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
if (likely (cache))
cache->clear ();
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) 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
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); #endif
if (index == NOT_COVERED) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset_fast (buffer->idx); skippy_iter.reset_fast (buffer->idx);

View File

@ -123,12 +123,61 @@ struct PairPosFormat2_4 : ValueBase
const Coverage &get_coverage () const { return this+coverage; } const Coverage &get_coverage () const { return this+coverage; }
bool apply (hb_ot_apply_context_t *c) const struct pair_pos_cache_t
{
hb_ot_lookup_cache_t coverage;
hb_ot_lookup_cache_t first;
hb_ot_lookup_cache_t second;
};
unsigned cache_cost () const
{
return (this+coverage).cost () + (this+classDef1).cost () + (this+classDef2).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
pair_pos_cache_t *cache = (pair_pos_cache_t *) hb_malloc (sizeof (pair_pos_cache_t));
if (likely (cache))
{
cache->coverage.clear ();
cache->first.clear ();
cache->second.clear ();
}
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
pair_pos_cache_t *cache = (pair_pos_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) 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
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
pair_pos_cache_t *cache = cached ? (pair_pos_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache ? &cache->coverage : nullptr);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); #endif
if (index == NOT_COVERED) return_trace (false);
hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input; hb_ot_apply_context_t::skipping_iterator_t &skippy_iter = c->iter_input;
skippy_iter.reset_fast (buffer->idx); skippy_iter.reset_fast (buffer->idx);
@ -139,8 +188,13 @@ struct PairPosFormat2_4 : ValueBase
return_trace (false); return_trace (false);
} }
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint, cache ? &cache->first : nullptr);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint, cache ? &cache->second : nullptr);
#else
unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint); unsigned int klass1 = (this+classDef1).get_class (buffer->cur().codepoint);
unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint); unsigned int klass2 = (this+classDef2).get_class (buffer->info[skippy_iter.idx].codepoint);
#endif
if (unlikely (klass1 >= class1Count || klass2 >= class2Count)) if (unlikely (klass1 >= class1Count || klass2 >= class2Count))
{ {
buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1); buffer->unsafe_to_concat (buffer->idx, skippy_iter.idx + 1);

View File

@ -67,7 +67,7 @@ struct SinglePosFormat1 : ValueBase
TRACE_APPLY (this); TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ()) if (HB_BUFFER_MESSAGE_MORE && c->buffer->messaging ())
{ {

View File

@ -66,7 +66,7 @@ struct SinglePosFormat2 : ValueBase
TRACE_APPLY (this); TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
if (unlikely (index >= valueCount)) return_trace (false); if (unlikely (index >= valueCount)) return_trace (false);

View File

@ -74,7 +74,7 @@ struct AlternateSubstFormat1_2
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
return_trace ((this+alternateSet[index]).apply (c)); return_trace ((this+alternateSet[index]).apply (c));
} }

View File

@ -78,12 +78,49 @@ struct LigatureSubstFormat1_2
return lig_set.would_apply (c); return lig_set.would_apply (c);
} }
bool apply (hb_ot_apply_context_t *c) const unsigned cache_cost () const
{
return (this+coverage).cost ();
}
static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{
switch (op)
{
case hb_ot_lookup_cache_op_t::CREATE:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) hb_malloc (sizeof (hb_ot_lookup_cache_t));
if (likely (cache))
cache->clear ();
return cache;
}
case hb_ot_lookup_cache_op_t::ENTER:
return (void *) true;
case hb_ot_lookup_cache_op_t::LEAVE:
return nullptr;
case hb_ot_lookup_cache_op_t::DESTROY:
{
hb_ot_lookup_cache_t *cache = (hb_ot_lookup_cache_t *) p;
hb_free (cache);
return nullptr;
}
}
return nullptr;
}
bool apply_cached (hb_ot_apply_context_t *c) 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
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
hb_buffer_t *buffer = c->buffer;
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (likely (index == NOT_COVERED)) return_trace (false); hb_ot_lookup_cache_t *cache = cached ? (hb_ot_lookup_cache_t *) c->lookup_accel->cache : nullptr;
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint, cache);
#else
unsigned int index = (this+coverage).get_coverage (buffer->cur().codepoint);
#endif
if (index == NOT_COVERED) return_trace (false);
const auto &lig_set = this+ligatureSet[index]; const auto &lig_set = this+ligatureSet[index];
return_trace (lig_set.apply (c)); return_trace (lig_set.apply (c));

View File

@ -66,7 +66,7 @@ struct MultipleSubstFormat1_2
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
return_trace ((this+sequence[index]).apply (c)); return_trace ((this+sequence[index]).apply (c));
} }

View File

@ -112,7 +112,7 @@ struct ReverseChainSingleSubstFormat1
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur ().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL)) if (unlikely (c->nesting_level_left != HB_MAX_NESTING_LEVEL))
return_trace (false); /* No chaining to this type */ return_trace (false); /* No chaining to this type */

View File

@ -128,7 +128,7 @@ struct SingleSubstFormat1_3
TRACE_APPLY (this); TRACE_APPLY (this);
hb_codepoint_t glyph_id = c->buffer->cur().codepoint; hb_codepoint_t glyph_id = c->buffer->cur().codepoint;
unsigned int index = (this+coverage).get_coverage (glyph_id); unsigned int index = (this+coverage).get_coverage (glyph_id);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
hb_codepoint_t d = deltaGlyphID; hb_codepoint_t d = deltaGlyphID;
hb_codepoint_t mask = get_mask (); hb_codepoint_t mask = get_mask ();

View File

@ -104,7 +104,7 @@ struct SingleSubstFormat2_4
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
if (unlikely (index >= substitute.len)) return_trace (false); if (unlikely (index >= substitute.len)) return_trace (false);

View File

@ -29,6 +29,9 @@
#ifndef OT_LAYOUT_TYPES_HH #ifndef OT_LAYOUT_TYPES_HH
#define OT_LAYOUT_TYPES_HH #define OT_LAYOUT_TYPES_HH
using hb_ot_lookup_cache_t = hb_cache_t<15, 8, 7>;
static_assert (sizeof (hb_ot_lookup_cache_t) == 256, "");
namespace OT { namespace OT {
namespace Layout { namespace Layout {

View File

@ -3,7 +3,6 @@
#ifndef HB_NO_VAR_COMPOSITES #ifndef HB_NO_VAR_COMPOSITES
#include "../../../hb-draw.hh" #include "../../../hb-draw.hh"
#include "../../../hb-geometry.hh"
#include "../../../hb-ot-layout-common.hh" #include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-layout-gdef-table.hh" #include "../../../hb-ot-layout-gdef-table.hh"
@ -133,8 +132,9 @@ VarComponent::get_path_at (hb_font_t *font,
hb_codepoint_t parent_gid, hb_codepoint_t parent_gid,
hb_draw_session_t &draw_session, hb_draw_session_t &draw_session,
hb_array_t<const int> coords, hb_array_t<const int> coords,
hb_transform_t total_transform,
hb_ubytes_t total_record, hb_ubytes_t total_record,
hb_set_t *visited, hb_decycler_t *decycler,
signed *edges_left, signed *edges_left,
signed depth_left, signed depth_left,
VarRegionList::cache_t *cache) const VarRegionList::cache_t *cache) const
@ -312,26 +312,14 @@ VarComponent::get_path_at (hb_font_t *font,
if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y)) if (!(flags & (unsigned) flags_t::HAVE_SCALE_Y))
transform.scaleY = transform.scaleX; transform.scaleY = transform.scaleX;
// Scale the transform by the font's scale total_transform.transform (transform.to_transform ());
float x_scale = font->x_multf; total_transform.scale (font->x_mult ? 1.f / font->x_multf : 0.f,
float y_scale = font->y_multf; font->y_mult ? 1.f / font->y_multf : 0.f);
transform.translateX *= x_scale;
transform.translateY *= y_scale;
transform.tCenterX *= x_scale;
transform.tCenterY *= y_scale;
// Build a transforming pen to apply the transform.
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
hb_transforming_pen_context_t context {transform.to_transform (),
draw_session.funcs,
draw_session.draw_data,
&draw_session.st};
hb_draw_session_t transformer_session {transformer_funcs, &context};
VARC.get_path_at (font, gid, VARC.get_path_at (font, gid,
transformer_session, component_coords, draw_session, component_coords, total_transform,
parent_gid, parent_gid,
visited, edges_left, depth_left - 1); decycler, edges_left, depth_left - 1);
} }
#undef PROCESS_TRANSFORM_COMPONENTS #undef PROCESS_TRANSFORM_COMPONENTS
@ -340,6 +328,71 @@ VarComponent::get_path_at (hb_font_t *font,
return hb_ubytes_t (record, end - record); return hb_ubytes_t (record, end - record);
} }
bool
VARC::get_path_at (hb_font_t *font,
hb_codepoint_t glyph,
hb_draw_session_t &draw_session,
hb_array_t<const int> coords,
hb_transform_t transform,
hb_codepoint_t parent_glyph,
hb_decycler_t *decycler,
signed *edges_left,
signed depth_left) const
{
// Don't recurse on the same glyph.
unsigned idx = glyph == parent_glyph ?
NOT_COVERED :
(this+coverage).get_coverage (glyph);
if (idx == NOT_COVERED)
{
// Build a transforming pen to apply the transform.
hb_draw_funcs_t *transformer_funcs = hb_transforming_pen_get_funcs ();
hb_transforming_pen_context_t context {transform,
draw_session.funcs,
draw_session.draw_data,
&draw_session.st};
hb_draw_session_t transformer_session {transformer_funcs, &context};
hb_draw_session_t &shape_draw_session = transform.is_identity () ? draw_session : transformer_session;
if (!font->face->table.glyf->get_path_at (font, glyph, shape_draw_session, coords))
#ifndef HB_NO_CFF
if (!font->face->table.cff2->get_path_at (font, glyph, shape_draw_session, coords))
if (!font->face->table.cff1->get_path (font, glyph, shape_draw_session)) // Doesn't have variations
#endif
return false;
return true;
}
if (depth_left <= 0)
return true;
if (*edges_left <= 0)
return true;
(*edges_left)--;
hb_decycler_node_t node (*decycler);
if (unlikely (!node.visit (glyph)))
return true;
hb_ubytes_t record = (this+glyphRecords)[idx];
VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
(this+varStore).create_cache ()
: nullptr;
transform.scale (font->x_multf, font->y_multf);
VarCompositeGlyph::get_path_at (font, glyph,
draw_session, coords, transform,
record,
decycler, edges_left, depth_left,
cache);
(this+varStore).destroy_cache (cache);
return true;
}
//} // namespace Var //} // namespace Var
} // namespace OT } // namespace OT

View File

@ -1,6 +1,8 @@
#ifndef OT_VAR_VARC_VARC_HH #ifndef OT_VAR_VARC_VARC_HH
#define OT_VAR_VARC_VARC_HH #define OT_VAR_VARC_VARC_HH
#include "../../../hb-decycler.hh"
#include "../../../hb-geometry.hh"
#include "../../../hb-ot-layout-common.hh" #include "../../../hb-ot-layout-common.hh"
#include "../../../hb-ot-glyf-table.hh" #include "../../../hb-ot-glyf-table.hh"
#include "../../../hb-ot-cff2-table.hh" #include "../../../hb-ot-cff2-table.hh"
@ -46,8 +48,9 @@ struct VarComponent
hb_codepoint_t parent_gid, hb_codepoint_t parent_gid,
hb_draw_session_t &draw_session, hb_draw_session_t &draw_session,
hb_array_t<const int> coords, hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record, hb_ubytes_t record,
hb_set_t *visited, hb_decycler_t *decycler,
signed *edges_left, signed *edges_left,
signed depth_left, signed depth_left,
VarRegionList::cache_t *cache = nullptr) const; VarRegionList::cache_t *cache = nullptr) const;
@ -60,8 +63,9 @@ struct VarCompositeGlyph
hb_codepoint_t glyph, hb_codepoint_t glyph,
hb_draw_session_t &draw_session, hb_draw_session_t &draw_session,
hb_array_t<const int> coords, hb_array_t<const int> coords,
hb_transform_t transform,
hb_ubytes_t record, hb_ubytes_t record,
hb_set_t *visited, hb_decycler_t *decycler,
signed *edges_left, signed *edges_left,
signed depth_left, signed depth_left,
VarRegionList::cache_t *cache = nullptr) VarRegionList::cache_t *cache = nullptr)
@ -70,9 +74,9 @@ struct VarCompositeGlyph
{ {
const VarComponent &comp = * (const VarComponent *) (record.arrayZ); const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
record = comp.get_path_at (font, glyph, record = comp.get_path_at (font, glyph,
draw_session, coords, draw_session, coords, transform,
record, record,
visited, edges_left, depth_left, cache); decycler, edges_left, depth_left, cache);
} }
} }
}; };
@ -85,80 +89,32 @@ struct VARC
static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C'); static constexpr hb_tag_t tableTag = HB_TAG ('V', 'A', 'R', 'C');
bool HB_INTERNAL bool
get_path_at (hb_font_t *font, get_path_at (hb_font_t *font,
hb_codepoint_t glyph, hb_codepoint_t glyph,
hb_draw_session_t &draw_session, hb_draw_session_t &draw_session,
hb_array_t<const int> coords, hb_array_t<const int> coords,
hb_codepoint_t parent_glyph = HB_CODEPOINT_INVALID, hb_transform_t transform,
hb_set_t *visited = nullptr, hb_codepoint_t parent_glyph,
signed *edges_left = nullptr, hb_decycler_t *decycler,
signed depth_left = HB_MAX_NESTING_LEVEL) const signed *edges_left,
{ signed depth_left) const;
hb_set_t stack_set;
if (visited == nullptr)
visited = &stack_set;
signed stack_edges = HB_MAX_GRAPH_EDGE_COUNT;
if (edges_left == nullptr)
edges_left = &stack_edges;
// Don't recurse on the same glyph.
unsigned idx = glyph == parent_glyph ?
NOT_COVERED :
(this+coverage).get_coverage (glyph);
if (idx == NOT_COVERED)
{
if (!font->face->table.glyf->get_path_at (font, glyph, draw_session, coords))
#ifndef HB_NO_CFF
if (!font->face->table.cff2->get_path_at (font, glyph, draw_session, coords))
if (!font->face->table.cff1->get_path (font, glyph, draw_session)) // Doesn't have variations
#endif
return false;
return true;
}
if (depth_left <= 0)
return true;
if (*edges_left <= 0)
return true;
(*edges_left)--;
if (visited->has (glyph) || visited->in_error ())
return true;
visited->add (glyph);
hb_ubytes_t record = (this+glyphRecords)[idx];
VarRegionList::cache_t *cache = record.length >= 64 ? // Heuristic
(this+varStore).create_cache ()
: nullptr;
VarCompositeGlyph::get_path_at (font, glyph,
draw_session, coords,
record,
visited, edges_left, depth_left,
cache);
(this+varStore).destroy_cache (cache);
visited->del (glyph);
return true;
}
bool bool
get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const get_path (hb_font_t *font, hb_codepoint_t gid, hb_draw_session_t &draw_session) const
{ return get_path_at (font, gid, draw_session, hb_array (font->coords, font->num_coords)); }
bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{ {
funcs->push_clip_glyph (data, gid, font); hb_decycler_t decycler;
funcs->color (data, true, foreground); signed edges = HB_MAX_GRAPH_EDGE_COUNT;
funcs->pop_clip (data);
return true; return get_path_at (font,
} gid,
draw_session,
hb_array (font->coords, font->num_coords),
HB_TRANSFORM_IDENTITY,
HB_CODEPOINT_INVALID,
&decycler,
&edges,
HB_MAX_NESTING_LEVEL); }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {

View File

@ -190,7 +190,7 @@ struct SimpleGlyph
unsigned int num_points = endPtsOfContours[num_contours - 1] + 1; unsigned int num_points = endPtsOfContours[num_contours - 1] + 1;
unsigned old_length = points.length; unsigned old_length = points.length;
points.alloc (points.length + num_points + 4, true); // Allocate for phantom points, to avoid a possible copy points.alloc_exact (points.length + num_points + 4); // Allocate for phantom points, to avoid a possible copy
if (unlikely (!points.resize (points.length + num_points, false))) return false; if (unlikely (!points.resize (points.length + num_points, false))) return false;
auto points_ = points.as_array ().sub_array (old_length); auto points_ = points.as_array ().sub_array (old_length);
if (!phantom_only) if (!phantom_only)
@ -281,9 +281,9 @@ struct SimpleGlyph
unsigned num_points = all_points.length - 4; unsigned num_points = all_points.length - 4;
hb_vector_t<uint8_t> flags, x_coords, y_coords; hb_vector_t<uint8_t> flags, x_coords, y_coords;
if (unlikely (!flags.alloc (num_points, true))) return false; if (unlikely (!flags.alloc_exact (num_points))) return false;
if (unlikely (!x_coords.alloc (2*num_points, true))) return false; if (unlikely (!x_coords.alloc_exact (2*num_points))) return false;
if (unlikely (!y_coords.alloc (2*num_points, true))) return false; if (unlikely (!y_coords.alloc_exact (2*num_points))) return false;
unsigned lastflag = 255, repeat = 0; unsigned lastflag = 255, repeat = 0;
int prev_x = 0, prev_y = 0; int prev_x = 0, prev_y = 0;

View File

@ -94,7 +94,7 @@ struct glyf
} }
hb_vector_t<unsigned> padded_offsets; hb_vector_t<unsigned> padded_offsets;
if (unlikely (!padded_offsets.alloc (c->plan->new_to_old_gid_list.length, true))) if (unlikely (!padded_offsets.alloc_exact (c->plan->new_to_old_gid_list.length)))
return_trace (false); return_trace (false);
hb_vector_t<glyf_impl::SubsetGlyph> glyphs; hb_vector_t<glyf_impl::SubsetGlyph> glyphs;
@ -229,8 +229,61 @@ struct glyf_accelerator_t
if (consumer.is_consuming_contour_points ()) if (consumer.is_consuming_contour_points ())
{ {
for (auto &point : all_points.as_array ().sub_array (0, count)) auto *points = all_points.arrayZ;
consumer.consume_point (point);
if (false)
{
/* Our path-builder was designed to work with this simple loop.
* But FreeType and CoreText do it differently, so we match those
* with the other, more complicated, code branch below. */
for (unsigned i = 0; i < count; i++)
{
consumer.consume_point (points[i]);
if (points[i].is_end_point)
consumer.contour_end ();
}
}
else
{
for (unsigned i = 0; i < count; i++)
{
// Start of a contour.
if (points[i].flag & glyf_impl::SimpleGlyph::FLAG_ON_CURVE)
{
// First point is on-curve. Draw the contour.
for (; i < count; i++)
{
consumer.consume_point (points[i]);
if (points[i].is_end_point)
{
consumer.contour_end ();
break;
}
}
}
else
{
unsigned start = i;
// Find end of the contour.
for (; i < count; i++)
if (points[i].is_end_point)
break;
unsigned end = i;
// Enough to start from the end. Our path-builder takes care of the rest.
if (likely (end < count)) // Can only fail in case of alloc failure *maybe*.
consumer.consume_point (points[end]);
for (i = start; i < end; i++)
consumer.consume_point (points[i]);
consumer.contour_end ();
}
}
}
consumer.points_end (); consumer.points_end ();
} }
@ -303,6 +356,7 @@ struct glyf_accelerator_t
HB_ALWAYS_INLINE HB_ALWAYS_INLINE
void consume_point (const contour_point_t &point) { bounds.add (point); } void consume_point (const contour_point_t &point) { bounds.add (point); }
void contour_end () {}
void points_end () { bounds.get_extents (font, extents, scaled); } void points_end () { bounds.get_extents (font, extents, scaled); }
bool is_consuming_contour_points () { return extents; } bool is_consuming_contour_points () { return extents; }
@ -371,15 +425,6 @@ struct glyf_accelerator_t
return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents); return glyph_for_gid (gid).get_extents_without_var_scaled (font, *this, extents);
} }
bool paint_glyph (hb_font_t *font, hb_codepoint_t gid, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, gid, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
const glyf_impl::Glyph const glyf_impl::Glyph
glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const glyph_for_gid (hb_codepoint_t gid, bool needs_padding_removal = false) const
{ {
@ -439,7 +484,7 @@ glyf::_populate_subset_glyphs (const hb_subset_plan_t *plan,
hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const hb_vector_t<glyf_impl::SubsetGlyph>& glyphs /* OUT */) const
{ {
OT::glyf_accelerator_t glyf (plan->source); OT::glyf_accelerator_t glyf (plan->source);
if (!glyphs.alloc (plan->new_to_old_gid_list.length, true)) return false; if (!glyphs.alloc_exact (plan->new_to_old_gid_list.length)) return false;
for (const auto &pair : plan->new_to_old_gid_list) for (const auto &pair : plan->new_to_old_gid_list)
{ {

View File

@ -124,7 +124,9 @@ struct path_builder_t
} }
} }
if (unlikely (point.is_end_point)) }
void contour_end ()
{ {
if (first_offcurve && last_offcurve) if (first_offcurve && last_offcurve)
{ {
@ -175,7 +177,7 @@ struct path_builder_t
first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t (); first_oncurve = first_offcurve = last_offcurve = last_offcurve2 = optional_point_t ();
draw_session->close_path (); draw_session->close_path ();
} }
}
void points_end () {} void points_end () {}
bool is_consuming_contour_points () { return true; } bool is_consuming_contour_points () { return true; }

View File

@ -485,7 +485,7 @@ struct name
const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ, const hb_array_t<const NameRecord> all_names (this->table->nameRecordZ.arrayZ,
this->table->count); this->table->count);
this->names.alloc (all_names.length, true); this->names.alloc_exact (all_names.length);
for (unsigned int i = 0; i < all_names.length; i++) for (unsigned int i = 0; i < all_names.length; i++)
{ {

View File

@ -30,6 +30,10 @@
#include "hb-aat-layout.hh" #include "hb-aat-layout.hh"
#include "hb-aat-map.hh" #include "hb-aat-map.hh"
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-cache.hh"
#include "hb-bit-set.hh"
#include "hb-bit-page.hh"
namespace OT { namespace OT {
struct GDEF; struct GDEF;
@ -39,10 +43,11 @@ namespace AAT {
using namespace OT; using namespace OT;
#define HB_AAT_BUFFER_DIGEST_THRESHOLD 32
struct ankr; struct ankr;
using hb_aat_class_cache_t = hb_cache_t<15, 8, 7>;
static_assert (sizeof (hb_aat_class_cache_t) == 256, "");
struct hb_aat_apply_context_t : struct hb_aat_apply_context_t :
hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY> hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
{ {
@ -61,10 +66,12 @@ struct hb_aat_apply_context_t :
const ankr *ankr_table; const ankr *ankr_table;
const OT::GDEF *gdef_table; const OT::GDEF *gdef_table;
const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr; const hb_sorted_vector_t<hb_aat_map_t::range_flags_t> *range_flags = nullptr;
hb_set_digest_t buffer_digest = hb_set_digest_t::full (); bool using_buffer_glyph_set = false;
hb_set_digest_t machine_glyph_set = hb_set_digest_t::full (); hb_bit_set_t buffer_glyph_set;
hb_set_digest_t left_set = hb_set_digest_t::full (); const hb_bit_set_t *left_set = nullptr;
hb_set_digest_t right_set = hb_set_digest_t::full (); const hb_bit_set_t *right_set = nullptr;
const hb_bit_set_t *machine_glyph_set = nullptr;
hb_aat_class_cache_t *machine_class_cache = nullptr;
hb_mask_t subtable_flags = 0; hb_mask_t subtable_flags = 0;
/* Unused. For debug tracing only. */ /* Unused. For debug tracing only. */
@ -80,6 +87,25 @@ struct hb_aat_apply_context_t :
HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_); HB_INTERNAL void set_ankr_table (const AAT::ankr *ankr_table_);
void set_lookup_index (unsigned int i) { lookup_index = i; } void set_lookup_index (unsigned int i) { lookup_index = i; }
void setup_buffer_glyph_set ()
{
using_buffer_glyph_set = buffer->len >= 4;
if (using_buffer_glyph_set)
buffer->collect_codepoints (buffer_glyph_set);
}
bool buffer_intersects_machine () const
{
if (using_buffer_glyph_set)
return buffer_glyph_set.intersects (*machine_glyph_set);
// Faster for shorter buffers.
for (unsigned i = 0; i < buffer->len; i++)
if (machine_glyph_set->has (buffer->info[i].codepoint))
return true;
return false;
}
}; };
@ -108,6 +134,13 @@ struct LookupFormat0
{ {
glyphs.add_range (0, num_glyphs - 1); glyphs.add_range (0, num_glyphs - 1);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
{
for (unsigned i = 0; i < num_glyphs; i++)
if (filter (arrayZ[i]))
glyphs.add (i);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -140,8 +173,13 @@ struct LookupSegmentSingle
template <typename set_t> template <typename set_t>
void collect_glyphs (set_t &glyphs) const void collect_glyphs (set_t &glyphs) const
{ {
if (first == DELETED_GLYPH) if (first == DELETED_GLYPH) return;
return; glyphs.add_range (first, last);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (!filter (value)) return;
glyphs.add_range (first, last); glyphs.add_range (first, last);
} }
@ -182,6 +220,13 @@ struct LookupFormat2
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
segments[i].collect_glyphs (glyphs); segments[i].collect_glyphs (glyphs);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = segments.get_length ();
for (unsigned int i = 0; i < count; i++)
segments[i].collect_glyphs_filtered (glyphs, filter);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -217,10 +262,17 @@ struct LookupSegmentArray
template <typename set_t> template <typename set_t>
void collect_glyphs (set_t &glyphs) const void collect_glyphs (set_t &glyphs) const
{ {
if (first == DELETED_GLYPH) if (first == DELETED_GLYPH) return;
return;
glyphs.add_range (first, last); glyphs.add_range (first, last);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const void *base, const filter_t &filter) const
{
const auto &values = base+valuesZ;
for (hb_codepoint_t i = first; i <= last; i++)
if (filter (values[i - first]))
glyphs.add (i);
}
int cmp (hb_codepoint_t g) const int cmp (hb_codepoint_t g) const
{ return g < first ? -1 : g <= last ? 0 : +1; } { return g < first ? -1 : g <= last ? 0 : +1; }
@ -271,6 +323,13 @@ struct LookupFormat4
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
segments[i].collect_glyphs (glyphs); segments[i].collect_glyphs (glyphs);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = segments.get_length ();
for (unsigned i = 0; i < count; i++)
segments[i].collect_glyphs_filtered (glyphs, this, filter);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -303,8 +362,13 @@ struct LookupSingle
template <typename set_t> template <typename set_t>
void collect_glyphs (set_t &glyphs) const void collect_glyphs (set_t &glyphs) const
{ {
if (glyph == DELETED_GLYPH) if (glyph == DELETED_GLYPH) return;
return; glyphs.add (glyph);
}
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (!filter (value)) return;
glyphs.add (glyph); glyphs.add (glyph);
} }
@ -344,6 +408,13 @@ struct LookupFormat6
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
entries[i].collect_glyphs (glyphs); entries[i].collect_glyphs (glyphs);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
unsigned count = entries.get_length ();
for (unsigned i = 0; i < count; i++)
entries[i].collect_glyphs_filtered (glyphs, filter);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -379,12 +450,20 @@ struct LookupFormat8
template <typename set_t> template <typename set_t>
void collect_glyphs (set_t &glyphs) const void collect_glyphs (set_t &glyphs) const
{ {
if (unlikely (!glyphCount)) if (unlikely (!glyphCount)) return;
return; if (firstGlyph == DELETED_GLYPH) return;
if (firstGlyph == DELETED_GLYPH)
return;
glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1); glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, const filter_t &filter) const
{
if (unlikely (!glyphCount)) return;
if (firstGlyph == DELETED_GLYPH) return;
const T *p = valueArrayZ.arrayZ;
for (unsigned i = 0; i < glyphCount; i++)
if (filter (p[i]))
glyphs.add (firstGlyph + i);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -433,10 +512,8 @@ struct LookupFormat10
template <typename set_t> template <typename set_t>
void collect_glyphs (set_t &glyphs) const void collect_glyphs (set_t &glyphs) const
{ {
if (unlikely (!glyphCount)) if (unlikely (!glyphCount)) return;
return; if (firstGlyph == DELETED_GLYPH) return;
if (firstGlyph == DELETED_GLYPH)
return;
glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1); glyphs.add_range (firstGlyph, firstGlyph + glyphCount - 1);
} }
@ -501,6 +578,18 @@ struct Lookup
default:return; default:return;
} }
} }
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) {
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;
case 6: hb_barrier (); u.format6.collect_glyphs_filtered (glyphs, filter); return;
case 8: hb_barrier (); u.format8.collect_glyphs_filtered (glyphs, filter); return;
default:return;
}
}
typename T::type get_class (hb_codepoint_t glyph_id, typename T::type get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs, unsigned int num_glyphs,
@ -563,7 +652,7 @@ DECLARE_NULL_NAMESPACE_BYTES_TEMPLATE1 (AAT, Lookup, 2);
template <typename T> template <typename T>
struct Entry struct Entry
{ {
// This does seem like it's ever called. // This doesn't seem like it's ever called.
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -632,18 +721,47 @@ struct StateTable
{ {
(this+classTable).collect_glyphs (glyphs, num_glyphs); (this+classTable).collect_glyphs (glyphs, num_glyphs);
} }
template <typename set_t, typename table_t>
void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs, const table_t &table) const
{
unsigned num_classes = nClasses;
if (unlikely (num_classes > hb_bit_page_t::BITS))
{
(this+classTable).collect_glyphs (glyphs, num_glyphs);
return;
}
// Collect all classes going out from the start state.
hb_bit_page_t filter;
for (unsigned i = 0; i < num_classes; i++)
{
const auto &entry = get_entry (STATE_START_OF_TEXT, i);
if (new_state (entry.newState) == STATE_START_OF_TEXT &&
!table.is_action_initiable (entry) && !table.is_actionable (entry))
continue;
filter.add (i);
}
// And glyphs in those classes.
(this+classTable).collect_glyphs_filtered (glyphs, num_glyphs, filter);
}
int new_state (unsigned int newState) const int new_state (unsigned int newState) const
{ return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; } { return Types::extended ? newState : ((int) newState - (int) stateArrayTable) / (int) nClasses; }
template <typename set_t>
unsigned int get_class (hb_codepoint_t glyph_id, unsigned int get_class (hb_codepoint_t glyph_id,
unsigned int num_glyphs, unsigned int num_glyphs,
const set_t &glyphs) const hb_aat_class_cache_t *cache = nullptr) const
{ {
unsigned klass;
if (cache && cache->get (glyph_id, &klass)) return klass;
if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH; if (unlikely (glyph_id == DELETED_GLYPH)) return CLASS_DELETED_GLYPH;
if (!glyphs[glyph_id]) return CLASS_OUT_OF_BOUNDS; klass = (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS);
return (this+classTable).get_class (glyph_id, num_glyphs, CLASS_OUT_OF_BOUNDS); if (cache) cache->set (glyph_id, klass);
return klass;
} }
const Entry<Extra> *get_entries () const const Entry<Extra> *get_entries () const
@ -651,13 +769,14 @@ struct StateTable
const Entry<Extra> &get_entry (int state, unsigned int klass) const const Entry<Extra> &get_entry (int state, unsigned int klass) const
{ {
if (unlikely (klass >= nClasses)) unsigned n_classes = nClasses;
if (unlikely (klass >= n_classes))
klass = CLASS_OUT_OF_BOUNDS; klass = CLASS_OUT_OF_BOUNDS;
const HBUSHORT *states = (this+stateArrayTable).arrayZ; const HBUSHORT *states = (this+stateArrayTable).arrayZ;
const Entry<Extra> *entries = (this+entryTable).arrayZ; const Entry<Extra> *entries = (this+entryTable).arrayZ;
unsigned int entry = states[state * nClasses + klass]; unsigned int entry = states[state * n_classes + klass];
DEBUG_MSG (APPLY, nullptr, "e%u", entry); DEBUG_MSG (APPLY, nullptr, "e%u", entry);
return entries[entry]; return entries[entry];
@ -803,6 +922,13 @@ struct ClassTable
if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS) if (classArray.arrayZ[i] != CLASS_OUT_OF_BOUNDS)
glyphs.add (firstGlyph + i); glyphs.add (firstGlyph + i);
} }
template <typename set_t, typename filter_t>
void collect_glyphs_filtered (set_t &glyphs, unsigned num_glyphs, const filter_t &filter) const
{
for (unsigned i = 0; i < classArray.len; i++)
if (filter (classArray.arrayZ[i]))
glyphs.add (firstGlyph + i);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -918,7 +1044,7 @@ struct ExtendedTypes
} }
}; };
template <typename Types, typename EntryData> template <typename Types, typename EntryData, typename Flags>
struct StateTableDriver struct StateTableDriver
{ {
using StateTableT = StateTable<Types, EntryData>; using StateTableT = StateTable<Types, EntryData>;
@ -929,14 +1055,6 @@ struct StateTableDriver
machine (machine_), machine (machine_),
num_glyphs (face_->get_num_glyphs ()) {} num_glyphs (face_->get_num_glyphs ()) {}
template <typename context_t>
bool is_idempotent_on_all_out_of_bounds (context_t *c, hb_aat_apply_context_t *ac)
{
const auto entry = machine.get_entry (StateTableT::STATE_START_OF_TEXT, CLASS_OUT_OF_BOUNDS);
return !c->is_actionable (ac->buffer, this, entry) &&
machine.new_state (entry.newState) == StateTableT::STATE_START_OF_TEXT;
}
template <typename context_t> template <typename context_t>
void drive (context_t *c, hb_aat_apply_context_t *ac) void drive (context_t *c, hb_aat_apply_context_t *ac)
{ {
@ -977,7 +1095,7 @@ struct StateTableDriver
} }
unsigned int klass = likely (buffer->idx < buffer->len) ? unsigned int klass = likely (buffer->idx < buffer->len) ?
machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_glyph_set) : machine.get_class (buffer->cur().codepoint, num_glyphs, ac->machine_class_cache) :
(unsigned) CLASS_END_OF_TEXT; (unsigned) CLASS_END_OF_TEXT;
DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx); DEBUG_MSG (APPLY, nullptr, "c%u at %u", klass, buffer->idx);
const EntryT &entry = machine.get_entry (state, klass); const EntryT &entry = machine.get_entry (state, klass);
@ -1011,41 +1129,36 @@ struct StateTableDriver
* *
* https://github.com/harfbuzz/harfbuzz/issues/2860 * https://github.com/harfbuzz/harfbuzz/issues/2860
*/ */
const EntryT *wouldbe_entry;
const auto is_safe_to_break_extra = [&]() bool is_safe_to_break =
{ (
/* 2c. */
const auto &wouldbe_entry = machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass);
/* 2c'. */
if (c->is_actionable (buffer, this, wouldbe_entry))
return false;
/* 2c". */
return next_state == machine.new_state(wouldbe_entry.newState)
&& (entry.flags & context_t::DontAdvance) == (wouldbe_entry.flags & context_t::DontAdvance);
};
const auto is_safe_to_break = [&]()
{
/* 1. */ /* 1. */
if (c->is_actionable (buffer, this, entry)) !c->table->is_actionable (entry) &&
return false;
/* 2. */ /* 2. */
// This one is meh, I know... // This one is meh, I know...
const auto ok = (
state == StateTableT::STATE_START_OF_TEXT state == StateTableT::STATE_START_OF_TEXT
|| ((entry.flags & context_t::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT) || ((entry.flags & Flags::DontAdvance) && next_state == StateTableT::STATE_START_OF_TEXT)
|| is_safe_to_break_extra(); || (
if (!ok) /* 2c. */
return false; wouldbe_entry = &machine.get_entry(StateTableT::STATE_START_OF_TEXT, klass)
,
/* 2c'. */
!c->table->is_actionable (*wouldbe_entry) &&
/* 2c". */
(
next_state == machine.new_state(wouldbe_entry->newState) &&
(entry.flags & Flags::DontAdvance) == (wouldbe_entry->flags & Flags::DontAdvance)
)
)
) &&
/* 3. */ /* 3. */
return !c->is_actionable (buffer, this, machine.get_entry (state, CLASS_END_OF_TEXT)); !c->table->is_actionable (machine.get_entry (state, CLASS_END_OF_TEXT))
}; );
if (!is_safe_to_break () && buffer->backtrack_len () && buffer->idx < buffer->len) if (!is_safe_to_break && buffer->backtrack_len () && buffer->idx < buffer->len)
buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1); buffer->unsafe_to_break_from_outbuffer (buffer->backtrack_len () - 1, buffer->idx + 1);
c->transition (buffer, this, entry); c->transition (buffer, this, entry);
@ -1056,7 +1169,7 @@ struct StateTableDriver
if (buffer->idx == buffer->len || unlikely (!buffer->successful)) if (buffer->idx == buffer->len || unlikely (!buffer->successful))
break; break;
if (!(entry.flags & context_t::DontAdvance) || buffer->max_ops-- <= 0) if (!(entry.flags & Flags::DontAdvance) || buffer->max_ops-- <= 0)
(void) buffer->next_glyph (); (void) buffer->next_glyph ();
} }

View File

@ -112,10 +112,6 @@ struct KerxSubTableFormat0
if (header.coverage & header.Backwards) if (header.coverage & header.Backwards)
return_trace (false); return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream); hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -144,7 +140,7 @@ struct KerxSubTableFormat0
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ {
if (!c->left_set[left] || !c->right_set[right]) return 0; if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c); return table.get_kerning (left, right, c);
} }
}; };
@ -211,6 +207,9 @@ struct Format1Entry<false>
typedef void EntryData; typedef void EntryData;
static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & Push; }
static bool performAction (const Entry<EntryData> &entry) static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & Offset; } { return entry.flags & Offset; }
@ -227,14 +226,24 @@ struct KerxSubTableFormat1
typedef Format1Entry<Types::extended> Format1EntryT; typedef Format1Entry<Types::extended> Format1EntryT;
typedef typename Format1EntryT::EntryData EntryData; typedef typename Format1EntryT::EntryData EntryData;
struct driver_context_t enum Flags
{
static constexpr bool in_place = true;
enum
{ {
DontAdvance = Format1EntryT::DontAdvance, DontAdvance = Format1EntryT::DontAdvance,
}; };
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return Format1EntryT::initiateAction (entry);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return Format1EntryT::performAction (entry);
}
struct driver_context_t
{
static constexpr bool in_place = true;
driver_context_t (const KerxSubTableFormat1 *table_, driver_context_t (const KerxSubTableFormat1 *table_,
hb_aat_apply_context_t *c_) : hb_aat_apply_context_t *c_) :
c (c_), c (c_),
@ -246,12 +255,8 @@ struct KerxSubTableFormat1
depth (0), depth (0),
crossStream (table->header.coverage & table->header.CrossStream) {} crossStream (table->header.coverage & table->header.CrossStream) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return Format1EntryT::performAction (entry); }
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
unsigned int flags = entry.flags; unsigned int flags = entry.flags;
@ -351,9 +356,10 @@ struct KerxSubTableFormat1
} }
} }
private: public:
hb_aat_apply_context_t *c; hb_aat_apply_context_t *c;
const KerxSubTableFormat1 *table; const KerxSubTableFormat1 *table;
private:
const UnsizedArrayOf<FWORD> &kernAction; const UnsizedArrayOf<FWORD> &kernAction;
unsigned int stack[8]; unsigned int stack[8];
unsigned int depth; unsigned int depth;
@ -370,12 +376,7 @@ struct KerxSubTableFormat1
driver_context_t dc (this, c); driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->font->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
driver.drive (&dc, c); driver.drive (&dc, c);
@ -440,10 +441,6 @@ struct KerxSubTableFormat2
if (header.coverage & header.Backwards) if (header.coverage & header.Backwards)
return_trace (false); return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream); hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -469,7 +466,7 @@ struct KerxSubTableFormat2
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ {
if (!c->left_set[left] || !c->right_set[right]) return 0; if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c); return table.get_kerning (left, right, c);
} }
}; };
@ -513,9 +510,6 @@ struct KerxSubTableFormat4
DEFINE_SIZE_STATIC (2); DEFINE_SIZE_STATIC (2);
}; };
struct driver_context_t
{
static constexpr bool in_place = true;
enum Flags enum Flags
{ {
Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */ Mark = 0x8000, /* If set, remember this glyph as the marked glyph. */
@ -524,6 +518,18 @@ struct KerxSubTableFormat4
Reserved = 0x3FFF, /* Not used; set to 0. */ Reserved = 0x3FFF, /* Not used; set to 0. */
}; };
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & Mark);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return entry.data.ankrActionIndex != 0xFFFF;
}
struct driver_context_t
{
static constexpr bool in_place = true;
enum SubTableFlags enum SubTableFlags
{ {
ActionType = 0xC0000000, /* A two-bit field containing the action type. */ ActionType = 0xC0000000, /* A two-bit field containing the action type. */
@ -533,20 +539,17 @@ struct KerxSubTableFormat4
* point table. */ * point table. */
}; };
driver_context_t (const KerxSubTableFormat4 *table, driver_context_t (const KerxSubTableFormat4 *table_,
hb_aat_apply_context_t *c_) : hb_aat_apply_context_t *c_) :
c (c_), c (c_),
table (table_),
action_type ((table->flags & ActionType) >> 30), action_type ((table->flags & ActionType) >> 30),
ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))), ankrData ((HBUINT16 *) ((const char *) &table->machine + (table->flags & Offset))),
mark_set (false), mark_set (false),
mark (0) {} mark (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry)
{ return entry.data.ankrActionIndex != 0xFFFF; }
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len) if (mark_set && entry.data.ankrActionIndex != 0xFFFF && buffer->idx < buffer->len)
@ -634,8 +637,10 @@ struct KerxSubTableFormat4
} }
} }
private: public:
hb_aat_apply_context_t *c; hb_aat_apply_context_t *c;
const KerxSubTableFormat4 *table;
private:
unsigned int action_type; unsigned int action_type;
const HBUINT16 *ankrData; const HBUINT16 *ankrData;
bool mark_set; bool mark_set;
@ -648,12 +653,7 @@ struct KerxSubTableFormat4
driver_context_t dc (this, c); driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->font->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->font->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) &&
!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
driver.drive (&dc, c); driver.drive (&dc, c);
@ -735,10 +735,6 @@ struct KerxSubTableFormat6
if (header.coverage & header.Backwards) if (header.coverage & header.Backwards)
return_trace (false); return_trace (false);
if (!(c->buffer_digest.may_have (c->left_set) &&
c->buffer_digest.may_have (c->right_set)))
return_trace (false);
accelerator_t accel (*this, c); accelerator_t accel (*this, c);
hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream); hb_kern_machine_t<accelerator_t> machine (accel, header.coverage & header.CrossStream);
machine.kern (c->font, c->buffer, c->plan->kern_mask); machine.kern (c->font, c->buffer, c->plan->kern_mask);
@ -793,7 +789,7 @@ struct KerxSubTableFormat6
int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const int get_kerning (hb_codepoint_t left, hb_codepoint_t right) const
{ {
if (!c->left_set[left] || !c->right_set[right]) return 0; if (!(*c->left_set)[left] || !(*c->right_set)[right]) return 0;
return table.get_kerning (left, right, c); return table.get_kerning (left, right, c);
} }
}; };
@ -925,7 +921,7 @@ struct KerxSubTable
* The 'kerx' Table * The 'kerx' Table
*/ */
using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_set_digest_t, hb_set_digest_t>>; using kern_accelerator_data_t = hb_vector_t<hb_pair_t<hb_bit_set_t, hb_bit_set_t>>;
template <typename T> template <typename T>
struct KerxTable struct KerxTable
@ -985,15 +981,10 @@ struct KerxTable
} }
bool apply (AAT::hb_aat_apply_context_t *c, bool apply (AAT::hb_aat_apply_context_t *c,
const kern_accelerator_data_t *accel_data = nullptr) const const kern_accelerator_data_t &accel_data) const
{ {
c->buffer->unsafe_to_concat (); c->buffer->unsafe_to_concat ();
if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD)
c->buffer_digest = c->buffer->digest ();
else
c->buffer_digest = hb_set_digest_t::full ();
typedef typename T::SubTable SubTable; typedef typename T::SubTable SubTable;
bool ret = false; bool ret = false;
@ -1037,15 +1028,8 @@ struct KerxTable
if (reverse) if (reverse)
c->buffer->reverse (); c->buffer->reverse ();
if (accel_data) c->left_set = &accel_data[i].first;
{ c->right_set = &accel_data[i].second;
c->left_set = (*accel_data)[i].first;
c->right_set = (*accel_data)[i].second;
}
else
{
c->left_set = c->right_set = hb_set_digest_t::full ();
}
{ {
/* See comment in sanitize() for conditional here. */ /* See comment in sanitize() for conditional here. */
@ -1122,7 +1106,7 @@ struct KerxTable
unsigned int count = thiz()->tableCount; unsigned int count = thiz()->tableCount;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
{ {
hb_set_digest_t left_set, right_set; hb_bit_set_t left_set, right_set;
st->collect_glyphs (left_set, right_set, num_glyphs); st->collect_glyphs (left_set, right_set, num_glyphs);
accel_data.push (hb_pair (left_set, right_set)); accel_data.push (hb_pair (left_set, right_set));
st = &StructAfter<SubTable> (*st); st = &StructAfter<SubTable> (*st);
@ -1148,7 +1132,7 @@ struct KerxTable
bool apply (AAT::hb_aat_apply_context_t *c) const bool apply (AAT::hb_aat_apply_context_t *c) const
{ {
return table->apply (c, &accel_data); return table->apply (c, accel_data);
} }
hb_blob_ptr_t<T> table; hb_blob_ptr_t<T> table;

View File

@ -29,6 +29,7 @@
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-aat-layout-common.hh" #include "hb-aat-layout-common.hh"
#include "hb-ot-layout.hh"
#include "hb-ot-layout-common.hh" #include "hb-ot-layout-common.hh"
#include "hb-ot-layout-gdef-table.hh" #include "hb-ot-layout-gdef-table.hh"
#include "hb-aat-map.hh" #include "hb-aat-map.hh"
@ -53,9 +54,6 @@ struct RearrangementSubtable
typedef void EntryData; typedef void EntryData;
struct driver_context_t
{
static constexpr bool in_place = true;
enum Flags enum Flags
{ {
MarkFirst = 0x8000, /* If set, make the current glyph the first MarkFirst = 0x8000, /* If set, make the current glyph the first
@ -70,18 +68,26 @@ struct RearrangementSubtable
Verb = 0x000F, /* The type of rearrangement specified. */ Verb = 0x000F, /* The type of rearrangement specified. */
}; };
driver_context_t (const RearrangementSubtable *table HB_UNUSED) : bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & MarkFirst);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return (entry.flags & Verb);
}
struct driver_context_t
{
static constexpr bool in_place = true;
driver_context_t (const RearrangementSubtable *table_) :
ret (false), ret (false),
table (table_),
start (0), end (0) {} start (0), end (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{
return (entry.flags & Verb) && start < end;
}
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
unsigned int flags = entry.flags; unsigned int flags = entry.flags;
@ -158,6 +164,7 @@ struct RearrangementSubtable
public: public:
bool ret; bool ret;
const RearrangementSubtable *table;
private: private:
unsigned int start; unsigned int start;
unsigned int end; unsigned int end;
@ -169,11 +176,13 @@ struct RearrangementSubtable
driver_context_t dc (this); driver_context_t dc (this);
StateTableDriver<Types, EntryData> driver (machine, c->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) && if (!c->buffer_intersects_machine ())
!c->buffer_digest.may_have (c->machine_glyph_set)) {
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false); return_trace (false);
}
driver.drive (&dc, c); driver.drive (&dc, c);
@ -207,9 +216,6 @@ struct ContextualSubtable
DEFINE_SIZE_STATIC (4); DEFINE_SIZE_STATIC (4);
}; };
struct driver_context_t
{
static constexpr bool in_place = true;
enum Flags enum Flags
{ {
SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */ SetMark = 0x8000, /* If set, make the current glyph the marked glyph. */
@ -218,28 +224,32 @@ struct ContextualSubtable
Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */ Reserved = 0x3FFF, /* These bits are reserved and should be set to 0. */
}; };
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return (entry.flags & SetMark);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
}
struct driver_context_t
{
static constexpr bool in_place = true;
driver_context_t (const ContextualSubtable *table_, driver_context_t (const ContextualSubtable *table_,
hb_aat_apply_context_t *c_) : hb_aat_apply_context_t *c_) :
ret (false), ret (false),
c (c_), c (c_),
table (table_),
gdef (*c->gdef_table), gdef (*c->gdef_table),
mark_set (false), mark_set (false),
has_glyph_classes (gdef.has_glyph_classes ()), has_glyph_classes (gdef.has_glyph_classes ()),
mark (0), mark (0),
table (table_),
subs (table+table->substitutionTables) {} subs (table+table->substitutionTables) {}
bool is_actionable (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver,
const Entry<EntryData> &entry) const
{
if (buffer->idx == buffer->len && !mark_set)
return false;
return entry.data.markIndex != 0xFFFF || entry.data.currentIndex != 0xFFFF;
}
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
/* Looks like CoreText applies neither mark nor current substitution for /* Looks like CoreText applies neither mark nor current substitution for
@ -271,8 +281,9 @@ struct ContextualSubtable
if (replacement) if (replacement)
{ {
buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len)); buffer->unsafe_to_break (mark, hb_min (buffer->idx + 1, buffer->len));
buffer->info[mark].codepoint = *replacement; hb_codepoint_t glyph = *replacement;
c->buffer_digest.add (*replacement); buffer->info[mark].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes) if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[mark], _hb_glyph_info_set_glyph_props (&buffer->info[mark],
gdef.get_glyph_props (*replacement)); gdef.get_glyph_props (*replacement));
@ -301,8 +312,9 @@ struct ContextualSubtable
} }
if (replacement) if (replacement)
{ {
buffer->info[idx].codepoint = *replacement; hb_codepoint_t glyph = *replacement;
c->buffer_digest.add (*replacement); buffer->info[idx].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes) if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&buffer->info[idx], _hb_glyph_info_set_glyph_props (&buffer->info[idx],
gdef.get_glyph_props (*replacement)); gdef.get_glyph_props (*replacement));
@ -318,13 +330,13 @@ struct ContextualSubtable
public: public:
bool ret; bool ret;
private:
hb_aat_apply_context_t *c; hb_aat_apply_context_t *c;
const ContextualSubtable *table;
private:
const OT::GDEF &gdef; const OT::GDEF &gdef;
bool mark_set; bool mark_set;
bool has_glyph_classes; bool has_glyph_classes;
unsigned int mark; unsigned int mark;
const ContextualSubtable *table;
const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs; const UnsizedListOfOffset16To<Lookup<HBGlyphID16>, HBUINT, void, false> &subs;
}; };
@ -334,11 +346,13 @@ struct ContextualSubtable
driver_context_t dc (this, c); driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) && if (!c->buffer_intersects_machine ())
!c->buffer_digest.may_have (c->machine_glyph_set)) {
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false); return_trace (false);
}
driver.drive (&dc, c); driver.drive (&dc, c);
@ -389,6 +403,16 @@ struct LigatureEntry;
template <> template <>
struct LigatureEntry<true> struct LigatureEntry<true>
{ {
struct EntryData
{
HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
* for processing this group, if indicated
* by the flags. */
public:
DEFINE_SIZE_STATIC (2);
};
enum Flags enum Flags
{ {
SetComponent = 0x8000, /* Push this glyph onto the component stack for SetComponent = 0x8000, /* Push this glyph onto the component stack for
@ -400,14 +424,8 @@ struct LigatureEntry<true>
Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */ Reserved = 0x1FFF, /* These bits are reserved and should be set to 0. */
}; };
struct EntryData static bool initiateAction (const Entry<EntryData> &entry)
{ { return entry.flags & SetComponent; }
HBUINT16 ligActionIndex; /* Index to the first ligActionTable entry
* for processing this group, if indicated
* by the flags. */
public:
DEFINE_SIZE_STATIC (2);
};
static bool performAction (const Entry<EntryData> &entry) static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & PerformAction; } { return entry.flags & PerformAction; }
@ -418,6 +436,8 @@ struct LigatureEntry<true>
template <> template <>
struct LigatureEntry<false> struct LigatureEntry<false>
{ {
typedef void EntryData;
enum Flags enum Flags
{ {
SetComponent = 0x8000, /* Push this glyph onto the component stack for SetComponent = 0x8000, /* Push this glyph onto the component stack for
@ -429,7 +449,8 @@ struct LigatureEntry<false>
* multiple of 4. */ * multiple of 4. */
}; };
typedef void EntryData; static bool initiateAction (const Entry<EntryData> &entry)
{ return entry.flags & SetComponent; }
static bool performAction (const Entry<EntryData> &entry) static bool performAction (const Entry<EntryData> &entry)
{ return entry.flags & Offset; } { return entry.flags & Offset; }
@ -447,13 +468,23 @@ struct LigatureSubtable
typedef LigatureEntry<Types::extended> LigatureEntryT; typedef LigatureEntry<Types::extended> LigatureEntryT;
typedef typename LigatureEntryT::EntryData EntryData; typedef typename LigatureEntryT::EntryData EntryData;
struct driver_context_t enum Flags
{
static constexpr bool in_place = false;
enum
{ {
DontAdvance = LigatureEntryT::DontAdvance, DontAdvance = LigatureEntryT::DontAdvance,
}; };
bool is_action_initiable (const Entry<EntryData> &entry) const
{
return LigatureEntryT::initiateAction (entry);
}
bool is_actionable (const Entry<EntryData> &entry) const
{
return LigatureEntryT::performAction (entry);
}
struct driver_context_t
{
static constexpr bool in_place = false;
enum LigActionFlags enum LigActionFlags
{ {
LigActionLast = 0x80000000, /* This is the last action in the list. This also LigActionLast = 0x80000000, /* This is the last action in the list. This also
@ -476,14 +507,8 @@ struct LigatureSubtable
ligature (table+table->ligature), ligature (table+table->ligature),
match_length (0) {} match_length (0) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{
return LigatureEntryT::performAction (entry);
}
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx); DEBUG_MSG (APPLY, nullptr, "Ligature transition at %u", buffer->idx);
@ -564,7 +589,7 @@ struct LigatureSubtable
{ {
DEBUG_MSG (APPLY, nullptr, "Skipping ligature component"); DEBUG_MSG (APPLY, nullptr, "Skipping ligature component");
if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return; if (unlikely (!buffer->move_to (match_positions[--match_length % ARRAY_LENGTH (match_positions)]))) return;
buffer->cur().unicode_props() |= UPROPS_MASK_IGNORABLE; _hb_glyph_info_set_default_ignorable (&buffer->cur());
if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return; if (unlikely (!buffer->replace_glyph (DELETED_GLYPH))) return;
} }
@ -581,9 +606,9 @@ struct LigatureSubtable
public: public:
bool ret; bool ret;
private:
hb_aat_apply_context_t *c; hb_aat_apply_context_t *c;
const LigatureSubtable *table; const LigatureSubtable *table;
private:
const UnsizedArrayOf<HBUINT32> &ligAction; const UnsizedArrayOf<HBUINT32> &ligAction;
const UnsizedArrayOf<HBUINT16> &component; const UnsizedArrayOf<HBUINT16> &component;
const UnsizedArrayOf<HBGlyphID16> &ligature; const UnsizedArrayOf<HBGlyphID16> &ligature;
@ -597,11 +622,13 @@ struct LigatureSubtable
driver_context_t dc (this, c); driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) && if (!c->buffer_intersects_machine ())
!c->buffer_digest.may_have (c->machine_glyph_set)) {
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false); return_trace (false);
}
driver.drive (&dc, c); driver.drive (&dc, c);
@ -638,6 +665,12 @@ struct NoncontextualSubtable
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
if (!c->buffer_intersects_machine ())
{
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false);
}
const OT::GDEF &gdef (*c->gdef_table); const OT::GDEF &gdef (*c->gdef_table);
bool has_glyph_classes = gdef.has_glyph_classes (); bool has_glyph_classes = gdef.has_glyph_classes ();
@ -670,8 +703,9 @@ struct NoncontextualSubtable
const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs); const HBGlyphID16 *replacement = substitute.get_value (info[i].codepoint, num_glyphs);
if (replacement) if (replacement)
{ {
info[i].codepoint = *replacement; hb_codepoint_t glyph = *replacement;
c->buffer_digest.add (*replacement); info[i].codepoint = glyph;
c->buffer_glyph_set.add (glyph);
if (has_glyph_classes) if (has_glyph_classes)
_hb_glyph_info_set_glyph_props (&info[i], _hb_glyph_info_set_glyph_props (&info[i],
gdef.get_glyph_props (*replacement)); gdef.get_glyph_props (*replacement));
@ -682,6 +716,12 @@ struct NoncontextualSubtable
return_trace (ret); return_trace (ret);
} }
template <typename set_t>
void collect_initial_glyphs (set_t &glyphs, unsigned num_glyphs) const
{
substitute.collect_glyphs (glyphs, num_glyphs);
}
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
@ -715,9 +755,6 @@ struct InsertionSubtable
DEFINE_SIZE_STATIC (4); DEFINE_SIZE_STATIC (4);
}; };
struct driver_context_t
{
static constexpr bool in_place = false;
enum Flags enum Flags
{ {
SetMark = 0x8000, /* If set, mark the current glyph. */ SetMark = 0x8000, /* If set, mark the current glyph. */
@ -766,22 +803,30 @@ struct InsertionSubtable
* marked location is 31 glyphs. */ * marked location is 31 glyphs. */
}; };
driver_context_t (const InsertionSubtable *table, bool is_action_initiable (const Entry<EntryData> &entry) const
hb_aat_apply_context_t *c_) : {
ret (false), return (entry.flags & SetMark);
c (c_), }
mark (0), bool is_actionable (const Entry<EntryData> &entry) const
insertionAction (table+table->insertionAction) {}
bool is_actionable (hb_buffer_t *buffer HB_UNUSED,
StateTableDriver<Types, EntryData> *driver HB_UNUSED,
const Entry<EntryData> &entry) const
{ {
return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) && return (entry.flags & (CurrentInsertCount | MarkedInsertCount)) &&
(entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF); (entry.data.currentInsertIndex != 0xFFFF ||entry.data.markedInsertIndex != 0xFFFF);
} }
struct driver_context_t
{
static constexpr bool in_place = false;
driver_context_t (const InsertionSubtable *table_,
hb_aat_apply_context_t *c_) :
ret (false),
c (c_),
table (table_),
mark (0),
insertionAction (table+table->insertionAction) {}
void transition (hb_buffer_t *buffer, void transition (hb_buffer_t *buffer,
StateTableDriver<Types, EntryData> *driver, StateTableDriver<Types, EntryData, Flags> *driver,
const Entry<EntryData> &entry) const Entry<EntryData> &entry)
{ {
unsigned int flags = entry.flags; unsigned int flags = entry.flags;
@ -807,7 +852,7 @@ struct InsertionSubtable
/* TODO We ignore KashidaLike setting. */ /* TODO We ignore KashidaLike setting. */
if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return; if (unlikely (!buffer->replace_glyphs (0, count, glyphs))) return;
for (unsigned int i = 0; i < count; i++) for (unsigned int i = 0; i < count; i++)
c->buffer_digest.add (glyphs[i]); c->buffer_glyph_set.add (glyphs[i]);
ret = true; ret = true;
if (buffer->idx < buffer->len && !before) if (buffer->idx < buffer->len && !before)
buffer->skip_glyph (); buffer->skip_glyph ();
@ -861,8 +906,9 @@ struct InsertionSubtable
public: public:
bool ret; bool ret;
private:
hb_aat_apply_context_t *c; hb_aat_apply_context_t *c;
const InsertionSubtable *table;
private:
unsigned int mark; unsigned int mark;
const UnsizedArrayOf<HBGlyphID16> &insertionAction; const UnsizedArrayOf<HBGlyphID16> &insertionAction;
}; };
@ -873,11 +919,13 @@ struct InsertionSubtable
driver_context_t dc (this, c); driver_context_t dc (this, c);
StateTableDriver<Types, EntryData> driver (machine, c->face); StateTableDriver<Types, EntryData, Flags> driver (machine, c->face);
if (driver.is_idempotent_on_all_out_of_bounds (&dc, c) && if (!c->buffer_intersects_machine ())
!c->buffer_digest.may_have (c->machine_glyph_set)) {
(void) c->buffer->message (c->font, "skipped chainsubtable because no glyph matches");
return_trace (false); return_trace (false);
}
driver.drive (&dc, c); driver.drive (&dc, c);
@ -935,24 +983,33 @@ struct hb_accelerate_subtables_context_t :
friend struct hb_aat_layout_lookup_accelerator_t; friend struct hb_aat_layout_lookup_accelerator_t;
public: public:
hb_set_digest_t digest; hb_bit_set_t glyph_set;
mutable hb_aat_class_cache_t class_cache;
template <typename T> template <typename T>
auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN auto init_ (const T &obj_, unsigned num_glyphs, hb_priority<1>) HB_AUTO_RETURN
( (
obj_.machine.collect_glyphs (this->digest, num_glyphs) obj_.machine.collect_initial_glyphs (glyph_set, num_glyphs, obj_)
) )
template <typename T> template <typename T>
void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>) void init_ (const T &obj_, unsigned num_glyphs, hb_priority<0>)
{ {
digest = digest.full (); obj_.collect_initial_glyphs (glyph_set, num_glyphs);
} }
template <typename T> template <typename T>
void init (const T &obj_, unsigned num_glyphs) void init (const T &obj_, unsigned num_glyphs)
{ {
glyph_set.init ();
init_ (obj_, num_glyphs, hb_prioritize); init_ (obj_, num_glyphs, hb_prioritize);
class_cache.clear ();
}
void
fini ()
{
glyph_set.fini ();
} }
}; };
@ -999,12 +1056,21 @@ struct hb_aat_layout_chain_accelerator_t
if (unlikely (!thiz)) if (unlikely (!thiz))
return nullptr; return nullptr;
thiz->count = count;
hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs); hb_accelerate_subtables_context_t c_accelerate_subtables (thiz->subtables, num_glyphs);
chain.dispatch (&c_accelerate_subtables); chain.dispatch (&c_accelerate_subtables);
return thiz; return thiz;
} }
void destroy ()
{
for (unsigned i = 0; i < count; i++)
subtables[i].fini ();
}
unsigned count;
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
}; };
@ -1152,15 +1218,19 @@ struct Chain
{ {
bool reverse; bool reverse;
if (hb_none (hb_iter (c->range_flags) | auto coverage = subtable->get_coverage ();
hb_map ([&subtable] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable->subFeatureFlags & (_.flags); })))
goto skip;
c->subtable_flags = subtable->subFeatureFlags;
c->machine_glyph_set = accel ? accel->subtables[i].digest : hb_set_digest_t::full ();
if (!(subtable->get_coverage() & ChainSubtable<Types>::AllDirections) && hb_mask_t subtable_flags = subtable->subFeatureFlags;
if (hb_none (hb_iter (c->range_flags) |
hb_map ([subtable_flags] (const hb_aat_map_t::range_flags_t _) -> bool { return subtable_flags & (_.flags); })))
goto skip;
c->subtable_flags = subtable_flags;
c->machine_glyph_set = accel ? &accel->subtables[i].glyph_set : &Null(hb_bit_set_t);
c->machine_class_cache = accel ? &accel->subtables[i].class_cache : nullptr;
if (!(coverage & ChainSubtable<Types>::AllDirections) &&
HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) != HB_DIRECTION_IS_VERTICAL (c->buffer->props.direction) !=
bool (subtable->get_coverage() & ChainSubtable<Types>::Vertical)) bool (coverage & ChainSubtable<Types>::Vertical))
goto skip; goto skip;
/* Buffer contents is always in logical direction. Determine if /* Buffer contents is always in logical direction. Determine if
@ -1190,9 +1260,9 @@ struct Chain
(the order opposite that of the characters, which (the order opposite that of the characters, which
may be right-to-left or left-to-right). may be right-to-left or left-to-right).
*/ */
reverse = subtable->get_coverage () & ChainSubtable<Types>::Logical ? reverse = coverage & ChainSubtable<Types>::Logical ?
bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) : bool (coverage & ChainSubtable<Types>::Backwards) :
bool (subtable->get_coverage () & ChainSubtable<Types>::Backwards) != bool (coverage & ChainSubtable<Types>::Backwards) !=
HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction); HB_DIRECTION_IS_BACKWARD (c->buffer->props.direction);
if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index)) if (!c->buffer->message (c->font, "start chainsubtable %u", c->lookup_index))
@ -1298,6 +1368,12 @@ struct mortmorx
hb_sanitize_context_t sc; hb_sanitize_context_t sc;
this->table = sc.reference_table<T> (face); this->table = sc.reference_table<T> (face);
if (unlikely (this->table->is_blocklisted (this->table.get_blob (), face)))
{
hb_blob_destroy (this->table.get_blob ());
this->table = hb_blob_get_empty ();
}
this->chain_count = table->get_chain_count (); this->chain_count = table->get_chain_count ();
this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels)); this->accels = (hb_atomic_ptr_t<hb_aat_layout_chain_accelerator_t> *) hb_calloc (this->chain_count, sizeof (*accels));
@ -1311,7 +1387,11 @@ struct mortmorx
~accelerator_t () ~accelerator_t ()
{ {
for (unsigned int i = 0; i < this->chain_count; i++) for (unsigned int i = 0; i < this->chain_count; i++)
{
if (this->accels[i])
this->accels[i]->destroy ();
hb_free (this->accels[i]); hb_free (this->accels[i]);
}
hb_free (this->accels); hb_free (this->accels);
this->table.destroy (); this->table.destroy ();
} }
@ -1367,7 +1447,6 @@ struct mortmorx
{ {
return chainCount; return chainCount;
} }
void apply (hb_aat_apply_context_t *c, void apply (hb_aat_apply_context_t *c,
const hb_aat_map_t &map, const hb_aat_map_t &map,
const accelerator_t &accel) const const accelerator_t &accel) const
@ -1376,10 +1455,7 @@ struct mortmorx
c->buffer->unsafe_to_concat (); c->buffer->unsafe_to_concat ();
if (c->buffer->len < HB_AAT_BUFFER_DIGEST_THRESHOLD) c->setup_buffer_glyph_set ();
c->buffer_digest = c->buffer->digest ();
else
c->buffer_digest = hb_set_digest_t::full ();
c->set_lookup_index (0); c->set_lookup_index (0);
const Chain<Types> *chain = &firstChain; const Chain<Types> *chain = &firstChain;
@ -1428,8 +1504,17 @@ struct mortmorx
DEFINE_SIZE_MIN (8); DEFINE_SIZE_MIN (8);
}; };
struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx> {}; struct morx : mortmorx<morx, ExtendedTypes, HB_AAT_TAG_morx>
struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort> {}; {
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
};
struct mort : mortmorx<mort, ObsoleteTypes, HB_AAT_TAG_mort>
{
HB_INTERNAL bool is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const;
};
struct morx_accelerator_t : morx::accelerator_t { struct morx_accelerator_t : morx::accelerator_t {
morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {} morx_accelerator_t (hb_face_t *face) : morx::accelerator_t (face) {}

View File

@ -48,17 +48,69 @@ struct TrackTableEntry
float get_track_value () const { return track.to_float (); } float get_track_value () const { return track.to_float (); }
int get_value (const void *base, unsigned int index, float interpolate_at (unsigned int idx,
unsigned int table_size) const float ptem,
{ return (base+valuesZ).as_array (table_size)[index]; } const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
float s0 = size_table[idx].to_float ();
float s1 = size_table[idx + 1].to_float ();
int v0 = values[idx];
int v1 = values[idx + 1];
// Deal with font bugs.
if (unlikely (s1 < s0))
{ hb_swap (s0, s1); hb_swap (v0, v1); }
if (unlikely (ptem < s0)) return v0;
if (unlikely (ptem > s1)) return v1;
if (unlikely (s0 == s1)) return (v0 + v1) * 0.5f;
float t = (ptem - s0) / (s1 - s0);
return v0 + t * (v1 - v0);
}
float get_value (float ptem,
const void *base,
hb_array_t<const F16DOT16> size_table) const
{
const FWORD *values = (base+valuesZ).arrayZ;
unsigned int n_sizes = size_table.length;
/*
* Choose size.
*/
if (!n_sizes) return 0.f;
if (n_sizes == 1) return values[0];
// At least two entries.
unsigned i;
for (i = 0; i < n_sizes; i++)
if (size_table[i].to_float () >= ptem)
break;
// Boundary conditions.
if (i == 0) return values[0];
if (i == n_sizes) return values[n_sizes - 1];
// Exact match.
if (size_table[i].to_float () == ptem) return values[i];
// Interpolate.
return interpolate_at (i - 1, ptem, base, size_table);
}
public: public:
bool sanitize (hb_sanitize_context_t *c, const void *base, bool sanitize (hb_sanitize_context_t *c,
unsigned int table_size) const const void *base,
unsigned int n_sizes) const
{ {
TRACE_SANITIZE (this); TRACE_SANITIZE (this);
return_trace (likely (c->check_struct (this) && return_trace (likely (c->check_struct (this) &&
(valuesZ.sanitize (c, base, table_size)))); (valuesZ.sanitize (c, base, n_sizes))));
} }
protected: protected:
@ -76,58 +128,38 @@ struct TrackTableEntry
struct TrackData struct TrackData
{ {
float interpolate_at (unsigned int idx, float get_tracking (const void *base, float ptem, float track = 0.f) const
float target_size,
const TrackTableEntry &trackTableEntry,
const void *base) const
{ {
unsigned int sizes = nSizes; unsigned count = nTracks;
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes); hb_array_t<const F16DOT16> size_table = (base+sizeTable).as_array (nSizes);
float s0 = size_table[idx].to_float (); if (!count) return 0.f;
float s1 = size_table[idx + 1].to_float (); if (count == 1) return trackTable[0].get_value (ptem, base, size_table);
float t = unlikely (s0 == s1) ? 0.f : (target_size - s0) / (s1 - s0);
return t * trackTableEntry.get_value (base, idx + 1, sizes) +
(1.f - t) * trackTableEntry.get_value (base, idx, sizes);
}
int get_tracking (const void *base, float ptem) const // At least two entries.
{
/*
* Choose track.
*/
const TrackTableEntry *trackTableEntry = nullptr;
unsigned int count = nTracks;
for (unsigned int i = 0; i < count; i++)
{
/* Note: Seems like the track entries are sorted by values. But the
* spec doesn't explicitly say that. It just mentions it in the example. */
/* For now we only seek for track entries with zero tracking value */ unsigned i = 0;
unsigned j = count - 1;
if (trackTable[i].get_track_value () == 0.f) // Find the two entries that track is between.
{ while (i + 1 < count && trackTable[i + 1].get_track_value () < track)
trackTableEntry = &trackTable[i]; i++;
break; while (j > 0 && trackTable[j - 1].get_track_value () > track)
} j--;
}
if (!trackTableEntry) return 0;
/* // Exact match.
* Choose size. if (i == j) return trackTable[i].get_value (ptem, base, size_table);
*/
unsigned int sizes = nSizes;
if (!sizes) return 0;
if (sizes == 1) return trackTableEntry->get_value (base, 0, sizes);
hb_array_t<const F16DOT16> size_table ((base+sizeTable).arrayZ, sizes); // Interpolate.
unsigned int size_index;
for (size_index = 0; size_index < sizes - 1; size_index++)
if (size_table[size_index].to_float () >= ptem)
break;
return roundf (interpolate_at (size_index ? size_index - 1 : 0, ptem, float t0 = trackTable[i].get_track_value ();
*trackTableEntry, base)); float t1 = trackTable[j].get_track_value ();
float t = (track - t0) / (t1 - t0);
float a = trackTable[i].get_value (ptem, base, size_table);
float b = trackTable[j].get_value (ptem, base, size_table);
return a + t * (b - a);
} }
bool sanitize (hb_sanitize_context_t *c, const void *base) const bool sanitize (hb_sanitize_context_t *c, const void *base) const
@ -158,45 +190,15 @@ struct trak
bool has_data () const { return version.to_int (); } bool has_data () const { return version.to_int (); }
bool apply (hb_aat_apply_context_t *c) const hb_position_t get_h_tracking (hb_font_t *font, float track = 0.f) const
{ {
TRACE_APPLY (this); float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
return font->em_scalef_x ((this+horizData).get_tracking (this, ptem, track));
hb_mask_t trak_mask = c->plan->trak_mask;
const float ptem = c->font->ptem;
if (unlikely (ptem <= 0.f))
return_trace (false);
hb_buffer_t *buffer = c->buffer;
if (HB_DIRECTION_IS_HORIZONTAL (buffer->props.direction))
{
const TrackData &trackData = this+horizData;
int tracking = trackData.get_tracking (this, ptem);
hb_position_t offset_to_add = c->font->em_scalef_x (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_x (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].x_advance += advance_to_add;
buffer->pos[start].x_offset += offset_to_add;
} }
} hb_position_t get_v_tracking (hb_font_t *font, float track = 0.f) const
else
{ {
const TrackData &trackData = this+vertData; float ptem = font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE;
int tracking = trackData.get_tracking (this, ptem); return font->em_scalef_y ((this+vertData).get_tracking (this, ptem, track));
hb_position_t offset_to_add = c->font->em_scalef_y (tracking / 2);
hb_position_t advance_to_add = c->font->em_scalef_y (tracking);
foreach_grapheme (buffer, start, end)
{
if (!(buffer->info[start].mask & trak_mask)) continue;
buffer->pos[start].y_advance += advance_to_add;
buffer->pos[start].y_offset += offset_to_add;
}
}
return_trace (true);
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const

View File

@ -34,9 +34,12 @@
#include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise. #include "hb-aat-layout-just-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-layout-kerx-table.hh" #include "hb-aat-layout-kerx-table.hh"
#include "hb-aat-layout-morx-table.hh" #include "hb-aat-layout-morx-table.hh"
#include "hb-aat-layout-trak-table.hh" #include "hb-aat-layout-trak-table.hh" // Just so we compile it; unused otherwise.
#include "hb-aat-ltag-table.hh" #include "hb-aat-ltag-table.hh"
#include "hb-ot-layout-gsub-table.hh"
#include "hb-ot-layout-gdef-table.hh"
/* /*
* hb_aat_apply_context_t * hb_aat_apply_context_t
@ -207,6 +210,36 @@ hb_aat_layout_find_feature_mapping (hb_tag_t tag)
*/ */
bool
AAT::morx::is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const
{
#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
return false;
#endif
switch HB_CODEPOINT_ENCODE3 (blob->length,
face->table.GSUB->table.get_length (),
face->table.GDEF->table.get_length ())
{
/* https://github.com/harfbuzz/harfbuzz/issues/4108
sha1sum:a71ca6813b7e56a772cffff7c24a5166b087197c AALMAGHRIBI.ttf */
case HB_CODEPOINT_ENCODE3 (19892, 2794, 340):
return true;
}
return false;
}
bool
AAT::mort::is_blocklisted (hb_blob_t *blob,
hb_face_t *face) const
{
#ifdef HB_NO_AAT_LAYOUT_BLOCKLIST
return false;
#endif
return false;
}
void void
hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper, hb_aat_layout_compile_map (const hb_aat_map_builder_t *mapper,
hb_aat_map_t *map) hb_aat_map_t *map)
@ -361,17 +394,6 @@ hb_aat_layout_has_tracking (hb_face_t *face)
return face->table.trak->has_data (); return face->table.trak->has_data ();
} }
void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer)
{
const AAT::trak& trak = *font->face->table.trak;
AAT::hb_aat_apply_context_t c (plan, font, buffer);
trak.apply (&c);
}
/** /**
* hb_aat_layout_get_feature_types: * hb_aat_layout_get_feature_types:
* @face: #hb_face_t to work upon * @face: #hb_face_t to work upon

View File

@ -32,6 +32,9 @@
#include "hb-ot-shape.hh" #include "hb-ot-shape.hh"
#include "hb-aat-ltag-table.hh" #include "hb-aat-ltag-table.hh"
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
struct hb_aat_feature_mapping_t struct hb_aat_feature_mapping_t
{ {
hb_tag_t otFeatureTag; hb_tag_t otFeatureTag;
@ -68,10 +71,5 @@ hb_aat_layout_position (const hb_ot_shape_plan_t *plan,
hb_font_t *font, hb_font_t *font,
hb_buffer_t *buffer); hb_buffer_t *buffer);
HB_INTERNAL void
hb_aat_layout_track (const hb_ot_shape_plan_t *plan,
hb_font_t *font,
hb_buffer_t *buffer);
#endif /* HB_AAT_LAYOUT_HH */ #endif /* HB_AAT_LAYOUT_HH */

View File

@ -88,22 +88,23 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
/* Sort features by start/end events. */ /* Sort features by start/end events. */
hb_vector_t<feature_event_t> feature_events; hb_vector_t<feature_event_t> feature_events;
feature_events.alloc_exact (features.length * 2 + 1);
for (unsigned int i = 0; i < features.length; i++) for (unsigned int i = 0; i < features.length; i++)
{ {
auto &feature = features[i]; auto &feature = features.arrayZ[i];
if (features[i].start == features[i].end) if (feature.start == feature.end)
continue; continue;
feature_event_t *event; feature_event_t *event;
event = feature_events.push (); event = feature_events.push ();
event->index = features[i].start; event->index = feature.start;
event->start = true; event->start = true;
event->feature = feature.info; event->feature = feature.info;
event = feature_events.push (); event = feature_events.push ();
event->index = features[i].end; event->index = feature.end;
event->start = false; event->start = false;
event->feature = feature.info; event->feature = feature.info;
} }
@ -139,12 +140,12 @@ hb_aat_map_builder_t::compile (hb_aat_map_t &m)
current_features.qsort (); current_features.qsort ();
unsigned int j = 0; unsigned int j = 0;
for (unsigned int i = 1; i < current_features.length; i++) for (unsigned int i = 1; i < current_features.length; i++)
if (current_features[i].type != current_features[j].type || if (current_features.arrayZ[i].type != current_features.arrayZ[j].type ||
/* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off /* Nonexclusive feature selectors come in even/odd pairs to turn a setting on/off
* respectively, so we mask out the low-order bit when checking for "duplicates" * respectively, so we mask out the low-order bit when checking for "duplicates"
* (selectors referring to the same feature setting) here. */ * (selectors referring to the same feature setting) here. */
(!current_features[i].is_exclusive && ((current_features[i].setting & ~1) != (current_features[j].setting & ~1)))) (!current_features.arrayZ[i].is_exclusive && ((current_features.arrayZ[i].setting & ~1) != (current_features.arrayZ[j].setting & ~1))))
current_features[++j] = current_features[i]; current_features.arrayZ[++j] = current_features.arrayZ[i];
current_features.shrink (j + 1); current_features.shrink (j + 1);
} }

View File

@ -286,7 +286,7 @@ HB_FUNCOBJ (hb_bool);
// Compression function for Merkle-Damgard construction. // Compression function for Merkle-Damgard construction.
// This function is generated using the framework provided. // This function is generated using the framework provided.
#define mix(h) ( \ #define fasthash_mix(h) ( \
(void) ((h) ^= (h) >> 23), \ (void) ((h) ^= (h) >> 23), \
(void) ((h) *= 0x2127599bf4325c37ULL), \ (void) ((h) *= 0x2127599bf4325c37ULL), \
(h) ^= (h) >> 47) (h) ^= (h) >> 47)
@ -310,7 +310,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
#pragma GCC diagnostic ignored "-Wcast-align" #pragma GCC diagnostic ignored "-Wcast-align"
v = * (const uint64_t *) (pos++); v = * (const uint64_t *) (pos++);
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
h ^= mix(v); h ^= fasthash_mix(v);
h *= m; h *= m;
} }
} }
@ -320,7 +320,7 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
while (pos != end) while (pos != end)
{ {
v = pos++->v; v = pos++->v;
h ^= mix(v); h ^= fasthash_mix(v);
h *= m; h *= m;
} }
} }
@ -336,11 +336,11 @@ static inline uint64_t fasthash64(const void *buf, size_t len, uint64_t seed)
case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH; case 3: v ^= (uint64_t)pos2[2] << 16; HB_FALLTHROUGH;
case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH; case 2: v ^= (uint64_t)pos2[1] << 8; HB_FALLTHROUGH;
case 1: v ^= (uint64_t)pos2[0]; case 1: v ^= (uint64_t)pos2[0];
h ^= mix(v); h ^= fasthash_mix(v);
h *= m; h *= m;
} }
return mix(h); return fasthash_mix(h);
} }
static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed) static inline uint32_t fasthash32(const void *buf, size_t len, uint32_t seed)

View File

@ -251,7 +251,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
if (end < start + 2) if (end < start + 2)
return; return;
for (unsigned lhs = start, rhs = end - 1; lhs < rhs; lhs++, rhs--) unsigned stop = start + (end - start) / 2;
for (unsigned lhs = start, rhs = end - 1; lhs < stop; lhs++, rhs--)
hb_swap (arrayZ[rhs], arrayZ[lhs]); hb_swap (arrayZ[rhs], arrayZ[lhs]);
} }

View File

@ -212,6 +212,7 @@ struct hb_atomic_ptr_t
T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); } T *get_acquire () const { return (T *) hb_atomic_ptr_impl_get ((void **) &v); }
bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); } bool cmpexch (const T *old, T *new_) const { return hb_atomic_ptr_impl_cmpexch ((void **) &v, (void *) old, (void *) new_); }
operator bool () const { return get_acquire () != nullptr; }
T * operator -> () const { return get_acquire (); } T * operator -> () const { return get_acquire (); }
template <typename C> operator C * () const { return get_acquire (); } template <typename C> operator C * () const { return get_acquire (); }

View File

@ -78,6 +78,28 @@ struct hb_vector_size_t
hb_vector_size_t operator ~ () const hb_vector_size_t operator ~ () const
{ return process (hb_bitwise_neg); } { return process (hb_bitwise_neg); }
operator bool () const
{
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
if (v[i])
return true;
return false;
}
operator unsigned int () const
{
unsigned int r = 0;
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
r += hb_popcount (v[i]);
return r;
}
bool operator == (const hb_vector_size_t &o) const
{
for (unsigned int i = 0; i < ARRAY_LENGTH (v); i++)
if (v[i] != o.v[i])
return false;
return true;
}
hb_array_t<const elt_t> iter () const hb_array_t<const elt_t> iter () const
{ return hb_array (v); } { return hb_array (v); }
@ -89,6 +111,8 @@ struct hb_vector_size_t
struct hb_bit_page_t struct hb_bit_page_t
{ {
hb_bit_page_t () { init0 (); }
void init0 () { v.init0 (); population = 0; } void init0 () { v.init0 (); population = 0; }
void init1 () { v.init1 (); population = PAGE_BITS; } void init1 () { v.init1 (); population = PAGE_BITS; }
@ -101,10 +125,9 @@ struct hb_bit_page_t
bool is_empty () const bool is_empty () const
{ {
if (has_population ()) return !population; if (has_population ()) return !population;
return bool empty = !v;
+ hb_iter (v) if (empty) population = 0;
| hb_none return empty;
;
} }
uint32_t hash () const uint32_t hash () const
{ {
@ -115,6 +138,10 @@ struct hb_bit_page_t
void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); } void del (hb_codepoint_t g) { elt (g) &= ~mask (g); dirty (); }
void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); } void set (hb_codepoint_t g, bool value) { if (value) add (g); else del (g); }
bool get (hb_codepoint_t g) const { return elt (g) & mask (g); } bool get (hb_codepoint_t g) const { return elt (g) & mask (g); }
bool may_have (hb_codepoint_t g) const { return get (g); }
bool operator [] (hb_codepoint_t g) const { return get (g); }
bool operator () (hb_codepoint_t g) const { return get (g); }
void add_range (hb_codepoint_t a, hb_codepoint_t b) void add_range (hb_codepoint_t a, hb_codepoint_t b)
{ {
@ -220,13 +247,17 @@ struct hb_bit_page_t
} }
bool operator == (const hb_bit_page_t &other) const { return is_equal (other); } bool operator == (const hb_bit_page_t &other) const { return is_equal (other); }
bool is_equal (const hb_bit_page_t &other) const bool is_equal (const hb_bit_page_t &other) const { return v == other.v; }
bool intersects (const hb_bit_page_t &other) const
{ {
for (unsigned i = 0; i < len (); i++) for (unsigned i = 0; i < len (); i++)
if (v[i] != other.v[i]) if (v[i] & other.v[i])
return false;
return true; return true;
return false;
} }
bool may_intersect (const hb_bit_page_t &other) const
{ return intersects (other); }
bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); } bool operator <= (const hb_bit_page_t &larger_page) const { return is_subset (larger_page); }
bool is_subset (const hb_bit_page_t &larger_page) const bool is_subset (const hb_bit_page_t &larger_page) const
{ {
@ -241,14 +272,10 @@ struct hb_bit_page_t
} }
bool has_population () const { return population != UINT_MAX; } bool has_population () const { return population != UINT_MAX; }
unsigned int get_population () const unsigned get_population () const
{ {
if (has_population ()) return population; if (has_population ()) return population;
population = return population = v;
+ hb_iter (v)
| hb_reduce ([] (unsigned pop, const elt_t &_) { return pop + hb_popcount (_); }, 0u)
;
return population;
} }
bool next (hb_codepoint_t *codepoint) const bool next (hb_codepoint_t *codepoint) const

View File

@ -126,6 +126,7 @@ struct hb_bit_set_invertible_t
{ unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); } { unlikely (inverted) ? (void) s.add_range (a, b) : s.del_range (a, b); }
bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; } bool get (hb_codepoint_t g) const { return s.get (g) ^ inverted; }
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */ /* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); } bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -139,6 +140,9 @@ struct hb_bit_set_invertible_t
hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range) hb_bit_set_invertible_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; } { add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_bit_set_invertible_t &other) const
{ return inverted || other.inverted || s.intersects (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{ {
hb_codepoint_t c = first - 1; hb_codepoint_t c = first - 1;

View File

@ -88,10 +88,11 @@ struct hb_bit_set_t
{ {
if (unlikely (!successful)) return false; if (unlikely (!successful)) return false;
if (pages.length == 0 && count == 1) if (pages.length < count && count <= 2)
exact_size = true; // Most sets are small and local exact_size = true; // Most sets are small and local
if (unlikely (!pages.resize (count, clear, exact_size) || !page_map.resize (count, clear, exact_size))) if (unlikely (!pages.resize (count, clear, exact_size) ||
!page_map.resize (count, clear)))
{ {
pages.resize (page_map.length, clear, exact_size); pages.resize (page_map.length, clear, exact_size);
successful = false; successful = false;
@ -297,9 +298,9 @@ struct hb_bit_set_t
unsigned int write_index = 0; unsigned int write_index = 0;
for (unsigned int i = 0; i < page_map.length; i++) for (unsigned int i = 0; i < page_map.length; i++)
{ {
int m = (int) page_map[i].major; int m = (int) page_map.arrayZ[i].major;
if (m < ds || de < m) if (m < ds || de < m)
page_map[write_index++] = page_map[i]; page_map.arrayZ[write_index++] = page_map.arrayZ[i];
} }
compact (compact_workspace, write_index); compact (compact_workspace, write_index);
resize (write_index); resize (write_index);
@ -345,6 +346,7 @@ struct hb_bit_set_t
return false; return false;
return page->get (g); return page->get (g);
} }
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */ /* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); } bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -358,6 +360,31 @@ struct hb_bit_set_t
hb_bit_set_t& operator << (const hb_codepoint_pair_t& range) hb_bit_set_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; } { add_range (range.first, range.second); return *this; }
bool intersects (const hb_bit_set_t &other) const
{
unsigned int na = pages.length;
unsigned int nb = other.pages.length;
unsigned int a = 0, b = 0;
for (; a < na && b < nb; )
{
if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
{
if (page_at (a).intersects (other.page_at (b)))
return true;
a++;
b++;
}
else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
a++;
else
b++;
}
return false;
}
bool may_intersect (const hb_bit_set_t &other) const
{ return intersects (other); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{ {
hb_codepoint_t c = first - 1; hb_codepoint_t c = first - 1;
@ -389,7 +416,7 @@ struct hb_bit_set_t
{ {
if (page_at (a).is_empty ()) { a++; continue; } if (page_at (a).is_empty ()) { a++; continue; }
if (other.page_at (b).is_empty ()) { b++; continue; } if (other.page_at (b).is_empty ()) { b++; continue; }
if (page_map[a].major != other.page_map[b].major || if (page_map.arrayZ[a].major != other.page_map.arrayZ[b].major ||
!page_at (a).is_equal (other.page_at (b))) !page_at (a).is_equal (other.page_at (b)))
return false; return false;
a++; a++;
@ -412,8 +439,8 @@ struct hb_bit_set_t
uint32_t spi = 0; uint32_t spi = 0;
for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++) for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set.page_map.length; lpi++)
{ {
uint32_t spm = page_map[spi].major; uint32_t spm = page_map.arrayZ[spi].major;
uint32_t lpm = larger_set.page_map[lpi].major; uint32_t lpm = larger_set.page_map.arrayZ[lpi].major;
auto sp = page_at (spi); auto sp = page_at (spi);
if (spm < lpm && !sp.is_empty ()) if (spm < lpm && !sp.is_empty ())
@ -503,7 +530,7 @@ struct hb_bit_set_t
for (; a < na && b < nb; ) for (; a < na && b < nb; )
{ {
if (page_map[a].major == other.page_map[b].major) if (page_map.arrayZ[a].major == other.page_map.arrayZ[b].major)
{ {
if (!passthru_left) if (!passthru_left)
{ {
@ -512,7 +539,7 @@ struct hb_bit_set_t
// passthru_left is set since no left side pages will be removed // passthru_left is set since no left side pages will be removed
// in that case. // in that case.
if (write_index < a) if (write_index < a)
page_map[write_index] = page_map[a]; page_map.arrayZ[write_index] = page_map.arrayZ[a];
write_index++; write_index++;
} }
@ -520,7 +547,7 @@ struct hb_bit_set_t
a++; a++;
b++; b++;
} }
else if (page_map[a].major < other.page_map[b].major) else if (page_map.arrayZ[a].major < other.page_map.arrayZ[b].major)
{ {
if (passthru_left) if (passthru_left)
count++; count++;
@ -765,8 +792,8 @@ struct hb_bit_set_t
unsigned int initial_size = size; unsigned int initial_size = size;
for (unsigned int i = start_page; i < page_map.length && size; i++) for (unsigned int i = start_page; i < page_map.length && size; i++)
{ {
uint32_t base = major_start (page_map[i].major); uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map[i].index].write (base, start_page_value, out, size); unsigned int n = pages[page_map.arrayZ[i].index].write (base, start_page_value, out, size);
out += n; out += n;
size -= n; size -= n;
start_page_value = 0; start_page_value = 0;
@ -814,8 +841,8 @@ struct hb_bit_set_t
hb_codepoint_t next_value = codepoint + 1; hb_codepoint_t next_value = codepoint + 1;
for (unsigned int i=start_page; i<page_map.length && size; i++) for (unsigned int i=start_page; i<page_map.length && size; i++)
{ {
uint32_t base = major_start (page_map[i].major); uint32_t base = major_start (page_map.arrayZ[i].major);
unsigned int n = pages[page_map[i].index].write_inverted (base, start_page_value, out, size, &next_value); unsigned int n = pages[page_map.arrayZ[i].index].write_inverted (base, start_page_value, out, size, &next_value);
out += n; out += n;
size -= n; size -= n;
start_page_value = 0; start_page_value = 0;
@ -846,8 +873,8 @@ struct hb_bit_set_t
unsigned count = pages.length; unsigned count = pages.length;
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
{ {
const auto& map = page_map[i]; const auto& map = page_map.arrayZ[i];
const auto& page = pages[map.index]; const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ()) if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_min (); return map.major * page_t::PAGE_BITS + page.get_min ();
@ -859,8 +886,8 @@ struct hb_bit_set_t
unsigned count = pages.length; unsigned count = pages.length;
for (signed i = count - 1; i >= 0; i--) for (signed i = count - 1; i >= 0; i--)
{ {
const auto& map = page_map[(unsigned) i]; const auto& map = page_map.arrayZ[(unsigned) i];
const auto& page = pages[map.index]; const auto& page = pages.arrayZ[map.index];
if (!page.is_empty ()) if (!page.is_empty ())
return map.major * page_t::PAGE_BITS + page.get_max (); return map.major * page_t::PAGE_BITS + page.get_max ();
@ -961,7 +988,7 @@ struct hb_bit_set_t
return nullptr; return nullptr;
last_page_lookup = i; last_page_lookup = i;
return &pages.arrayZ[page_map[i].index]; return &pages.arrayZ[page_map.arrayZ[i].index];
} }
page_t &page_at (unsigned int i) page_t &page_at (unsigned int i)
{ {

View File

@ -34,36 +34,36 @@
#line 36 "hb-buffer-deserialize-json.hh" #line 36 "hb-buffer-deserialize-json.hh"
static const unsigned char _deserialize_json_trans_keys[] = { static const unsigned char _deserialize_json_trans_keys[] = {
0u, 0u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 0u, 0u, 9u, 123u, 9u, 123u, 9u, 34u, 97u, 117u, 120u, 121u, 34u, 34u, 9u, 58u,
48u, 57u, 9u, 125u, 9u, 125u, 9u, 93u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u,
48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u,
9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 9u, 125u, 120u, 121u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u,
34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u, 34u, 34u, 9u, 58u, 9u, 57u, 48u, 57u, 9u, 125u, 9u, 125u, 108u, 108u, 34u, 34u,
9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 34u, 92u,
9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u, 9u, 125u, 34u, 92u, 9u, 125u, 9u, 125u, 34u, 34u, 9u, 58u, 9u, 57u, 9u, 125u,
9u, 123u, 0u, 0u, 0 9u, 93u, 9u, 123u, 0u, 0u, 0
}; };
static const char _deserialize_json_key_spans[] = { static const char _deserialize_json_key_spans[] = {
0, 115, 26, 21, 2, 1, 50, 49, 0, 115, 115, 26, 21, 2, 1, 50,
10, 117, 117, 85, 117, 1, 50, 49, 49, 10, 117, 117, 117, 1, 50, 49,
10, 117, 117, 1, 1, 50, 49, 117, 10, 117, 117, 1, 1, 50, 49, 117,
117, 2, 1, 50, 49, 10, 117, 117, 117, 2, 1, 50, 49, 10, 117, 117,
1, 50, 49, 10, 117, 117, 1, 1, 1, 50, 49, 10, 117, 117, 1, 1,
50, 49, 117, 117, 1, 50, 49, 59, 50, 49, 117, 117, 1, 50, 49, 59,
117, 59, 117, 117, 1, 50, 49, 117, 117, 59, 117, 117, 1, 50, 49, 117,
115, 0 85, 115, 0
}; };
static const short _deserialize_json_index_offsets[] = { static const short _deserialize_json_index_offsets[] = {
0, 0, 116, 143, 165, 168, 170, 221, 0, 0, 116, 232, 259, 281, 284, 286,
271, 282, 400, 518, 604, 722, 724, 775, 337, 387, 398, 516, 634, 752, 754, 805,
825, 836, 954, 1072, 1074, 1076, 1127, 1177, 855, 866, 984, 1102, 1104, 1106, 1157, 1207,
1295, 1413, 1416, 1418, 1469, 1519, 1530, 1648, 1325, 1443, 1446, 1448, 1499, 1549, 1560, 1678,
1766, 1768, 1819, 1869, 1880, 1998, 2116, 2118, 1796, 1798, 1849, 1899, 1910, 2028, 2146, 2148,
2120, 2171, 2221, 2339, 2457, 2459, 2510, 2560, 2150, 2201, 2251, 2369, 2487, 2489, 2540, 2590,
2620, 2738, 2798, 2916, 3034, 3036, 3087, 3137, 2650, 2768, 2828, 2946, 3064, 3066, 3117, 3167,
3255, 3371 3285, 3371, 3487
}; };
static const char _deserialize_json_indicies[] = { static const char _deserialize_json_indicies[] = {
@ -77,51 +77,51 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 3, 1, 2, 2, 2,
2, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 3, 3, 3,
3, 3, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 3, 1, 4, 1,
5, 1, 6, 7, 1, 8, 9, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 10, 1, 11, 12,
1, 13, 1, 13, 13, 13, 13, 13,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 13, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 14, 1, 14, 14,
14, 14, 14, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 14, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 15, 1, 1, 16, 17, 17,
17, 17, 17, 17, 17, 17, 17, 1,
18, 19, 19, 19, 19, 19, 19, 19,
19, 19, 1, 20, 20, 20, 20, 20,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 20, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3,
1, 4, 4, 4, 4, 4, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
4, 1, 5, 1, 6, 1, 7, 8,
1, 9, 10, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
11, 1, 12, 13, 1, 14, 1, 14,
14, 14, 14, 14, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 14, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
15, 1, 15, 15, 15, 15, 15, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 15, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 16, 1,
1, 17, 18, 18, 18, 18, 18, 18,
18, 18, 18, 1, 19, 20, 20, 20,
20, 20, 20, 20, 20, 20, 1, 21,
21, 21, 21, 21, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 21, 1, 1, 1, 1, 1, 1, 1, 21, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 22, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 22,
1, 23, 23, 23, 23, 23, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
23, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 3, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -131,24 +131,55 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 24, 1, 25, 1, 1, 1, 23, 1, 24, 24, 24,
25, 25, 25, 25, 1, 1, 1, 1, 24, 24, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 24, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
4, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 25, 1, 21, 21, 21, 21, 21,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 21, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 22, 1,
1, 1, 20, 20, 20, 20, 20, 20,
20, 20, 20, 20, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 23,
1, 26, 1, 26, 26, 26, 26, 26,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 25, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 26, 1, 1, 1, 1, 1, 1, 1, 26, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 27, 1, 27, 27,
27, 27, 27, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 27, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 28, 1, 1, 29, 30, 30,
30, 30, 30, 30, 30, 30, 30, 1,
31, 32, 32, 32, 32, 32, 32, 32,
32, 32, 1, 33, 33, 33, 33, 33,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 33, 1, 1, 1, 1, 1,
1, 1, 1, 27, 1, 20, 20, 20, 1, 1, 1, 1, 1, 1, 34, 1,
20, 20, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 20, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
21, 1, 1, 1, 19, 19, 19, 19,
19, 19, 19, 19, 19, 19, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -157,26 +188,15 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 22, 1, 28, 1, 28, 28, 28,
28, 28, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 35,
1, 33, 33, 33, 33, 33, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 29, 1, 33, 1, 1, 1, 1, 1, 1, 1,
29, 29, 29, 29, 29, 1, 1, 1, 1, 1, 1, 1, 34, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 29,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 30, 1, 1, 31,
32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
32, 1, 33, 34, 34, 34, 34, 34, 32, 32, 1, 1, 1, 1, 1, 1,
34, 34, 34, 34, 1, 35, 35, 35,
35, 35, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 35, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
36, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -184,41 +204,25 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 35, 1, 36,
1, 37, 1, 37, 37, 37, 37, 37,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 37, 1, 35, 35, 35, 35, 35, 1, 1, 37, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 35, 1, 1, 1, 1, 1, 1, 1, 1, 1, 38, 1, 38, 38,
1, 1, 1, 1, 1, 1, 36, 1, 38, 38, 38, 1, 1, 1, 1, 1,
1, 1, 34, 34, 34, 34, 34, 34,
34, 34, 34, 34, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 38, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 39, 40, 40,
1, 1, 1, 1, 1, 1, 1, 1, 40, 40, 40, 40, 40, 40, 40, 1,
1, 1, 1, 1, 1, 1, 1, 1, 41, 41, 41, 41, 41, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 37,
1, 38, 1, 39, 1, 39, 39, 39,
39, 39, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 39, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 40, 1,
40, 40, 40, 40, 40, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 40,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 41, 1, 1, 1, 1, 1, 1, 1, 41,
42, 42, 42, 42, 42, 42, 42, 42,
42, 1, 43, 43, 43, 43, 43, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 42, 1, 1, 1, 1,
1, 43, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 44, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -228,14 +232,13 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 45, 1, 1, 1, 1, 1, 43, 1, 41, 41,
43, 43, 43, 43, 43, 1, 1, 1, 41, 41, 41, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 43, 1, 1, 1, 1, 1, 41, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 44, 1, 1, 1, 46, 1, 42, 1, 1, 1, 44, 44, 44,
46, 46, 46, 46, 46, 46, 46, 46, 44, 44, 44, 44, 44, 44, 44, 1,
46, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -243,86 +246,116 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 45, 1, 47, 48, 1, 1, 1, 1, 1, 1, 1, 1,
1, 49, 1, 49, 49, 49, 49, 49, 1, 1, 43, 1, 45, 46, 1, 47,
1, 47, 47, 47, 47, 47, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 49, 1, 1, 1, 1, 1, 47, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 50, 1, 50, 50, 1, 1, 48, 1, 48, 48, 48, 48,
50, 50, 50, 1, 1, 1, 1, 1, 48, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 50, 1, 1, 1, 1, 1, 48, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 51, 1, 1, 52, 53, 53, 49, 1, 1, 50, 51, 51, 51, 51,
53, 53, 53, 53, 53, 53, 53, 1, 51, 51, 51, 51, 51, 1, 52, 53,
54, 55, 55, 55, 55, 55, 55, 55, 53, 53, 53, 53, 53, 53, 53, 53,
55, 55, 1, 56, 56, 56, 56, 56, 1, 54, 54, 54, 54, 54, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 56, 1, 1, 1, 1, 1, 54, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 55, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 56, 1, 54,
54, 54, 54, 54, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 54, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 55, 1, 1, 1, 53, 53,
53, 53, 53, 53, 53, 53, 53, 53,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 56, 1, 57, 1, 57,
57, 57, 57, 57, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 57, 1, 1, 1, 1, 1, 1, 1, 57, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
58, 1, 58, 58, 58, 58, 58, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 58, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 59, 1,
1, 60, 61, 61, 61, 61, 61, 61,
61, 61, 61, 1, 62, 63, 63, 63,
63, 63, 63, 63, 63, 63, 1, 64,
64, 64, 64, 64, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 64, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 65, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 58,
1, 56, 56, 56, 56, 56, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
56, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 57, 1, 1, 1,
55, 55, 55, 55, 55, 55, 55, 55,
55, 55, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 66, 1, 64, 64, 64,
64, 64, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 64, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 58, 1, 59, 65, 1, 1, 1, 63, 63, 63, 63,
1, 59, 59, 59, 59, 59, 1, 1, 63, 63, 63, 63, 63, 63, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
59, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 60, 1, 60, 60, 60, 60,
60, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 60, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
61, 1, 1, 62, 63, 63, 63, 63,
63, 63, 63, 63, 63, 1, 64, 65,
65, 65, 65, 65, 65, 65, 65, 65,
1, 66, 66, 66, 66, 66, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
66, 1, 1, 1, 1, 1, 1, 1, 1, 66, 1, 67, 1, 68, 1, 68,
1, 1, 1, 1, 67, 1, 1, 1, 68, 68, 68, 68, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 68, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
69, 1, 69, 69, 69, 69, 69, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 69, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 70, 71, 71, 71, 71, 71, 71,
71, 71, 71, 1, 72, 72, 72, 72,
72, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 72, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 73,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 68, 1, 66,
66, 66, 66, 66, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 66, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 67, 1, 1, 1, 65, 65,
65, 65, 65, 65, 65, 65, 65, 65,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -330,42 +363,48 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
74, 1, 72, 72, 72, 72, 72, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 68, 1, 69, 1, 70,
1, 70, 70, 70, 70, 70, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 72, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 73, 1, 1,
1, 75, 75, 75, 75, 75, 75, 75,
75, 75, 75, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
70, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 71, 1, 71, 71, 71, 71,
71, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 71, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 72, 73, 73, 73, 73,
73, 73, 73, 73, 73, 1, 74, 74,
74, 74, 74, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 74, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 75, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 74, 1,
76, 1, 76, 76, 76, 76, 76, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 76, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 77, 1, 77, 77, 77,
77, 77, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 77, 1, 78, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 79, 80, 80, 80,
80, 80, 80, 80, 80, 80, 1, 82,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 81, 81, 81, 81, 81, 81, 81,
81, 83, 81, 84, 84, 84, 84, 84,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 84, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 85, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 76, 1, 74, 74, 74, 74,
74, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 74, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 75,
1, 1, 1, 77, 77, 77, 77, 77,
77, 77, 77, 77, 77, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -373,34 +412,20 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 86,
1, 81, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
76, 1, 78, 1, 78, 78, 78, 78,
78, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 78, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 79, 1, 79,
79, 79, 79, 79, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 79, 1,
80, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 81, 82,
82, 82, 82, 82, 82, 82, 82, 82,
1, 84, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 83, 83, 83, 83, 83,
83, 83, 83, 85, 83, 86, 86, 86,
86, 86, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 86, 1, 1, 1, 1, 1, 1, 81, 1, 87, 87, 87,
87, 87, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
87, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 87, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
88, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -409,63 +434,65 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 88, 1, 83, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 89, 1, 87, 87, 87, 87, 87,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 87, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 88, 1,
1, 1, 90, 90, 90, 90, 90, 90,
90, 90, 90, 90, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 83, 1, 89,
89, 89, 89, 89, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 89, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 90, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 89,
1, 91, 1, 91, 91, 91, 91, 91,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 91, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 92, 1, 92, 92,
92, 92, 92, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 92, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 93, 94, 94,
94, 94, 94, 94, 94, 94, 94, 1,
87, 87, 87, 87, 87, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 91, 1, 89, 89, 89, 1, 1, 1, 1, 1, 1, 1, 87,
89, 89, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 89, 1, 1, 1, 1, 1, 1, 88, 1, 1, 1, 95,
95, 95, 95, 95, 95, 95, 95, 95,
95, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
90, 1, 1, 1, 92, 92, 92, 92,
92, 92, 92, 92, 92, 92, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 89, 1, 96, 96,
96, 96, 96, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 96, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 91, 1, 93, 1, 93, 93, 93, 1, 97, 1, 1, 1, 1, 1, 1,
93, 93, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 93, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 94, 1,
94, 94, 94, 94, 94, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 94,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 95, 1, 1, 98, 1, 2, 2, 2, 2,
96, 96, 96, 96, 96, 96, 96, 96, 2, 1, 1, 1, 1, 1, 1, 1,
96, 1, 89, 89, 89, 89, 89, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 2, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 89, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 90, 1, 1,
1, 97, 97, 97, 97, 97, 97, 97,
97, 97, 97, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
@ -473,54 +500,42 @@ static const char _deserialize_json_indicies[] = {
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 91, 1,
0, 0, 0, 0, 0, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 0,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 1,
1, 1, 1, 1, 1, 1, 1, 1, 1, 0
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 1, 1, 0
}; };
static const char _deserialize_json_trans_targs[] = { static const char _deserialize_json_trans_targs[] = {
1, 0, 2, 2, 3, 4, 19, 25, 1, 0, 2, 3, 3, 4, 5, 19,
38, 44, 52, 5, 13, 6, 7, 8, 25, 38, 44, 52, 6, 13, 7, 8,
9, 12, 9, 12, 10, 2, 11, 10, 9, 10, 12, 10, 12, 11, 3, 56,
11, 11, 56, 57, 14, 15, 16, 17, 11, 56, 14, 15, 16, 17, 18, 17,
18, 17, 18, 10, 2, 11, 20, 21, 18, 11, 3, 56, 20, 21, 22, 23,
22, 23, 24, 10, 2, 11, 24, 26, 24, 11, 3, 56, 24, 26, 32, 27,
32, 27, 28, 29, 30, 31, 30, 31, 28, 29, 30, 31, 30, 31, 11, 3,
10, 2, 11, 33, 34, 35, 36, 37, 56, 33, 34, 35, 36, 37, 36, 37,
36, 37, 10, 2, 11, 39, 40, 41, 11, 3, 56, 39, 40, 41, 42, 43,
42, 43, 10, 2, 11, 43, 45, 46, 11, 3, 56, 43, 45, 46, 47, 50,
47, 50, 51, 47, 48, 49, 10, 2, 51, 47, 48, 49, 11, 3, 56, 11,
11, 10, 2, 11, 51, 53, 54, 50, 3, 56, 51, 53, 54, 50, 55, 55,
55, 55 56, 57, 58
}; };
static const char _deserialize_json_trans_actions[] = { static const char _deserialize_json_trans_actions[] = {
0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0,
2, 2, 0, 0, 3, 3, 4, 0, 2, 2, 2, 0, 0, 3, 3, 4,
5, 0, 0, 0, 0, 0, 2, 2, 0, 5, 0, 0, 2, 2, 2, 0,
2, 0, 0, 6, 6, 7, 0, 0, 0, 6, 6, 7, 0, 0, 0, 2,
0, 2, 2, 8, 8, 9, 0, 0, 2, 8, 8, 9, 0, 0, 0, 0,
0, 0, 0, 2, 2, 2, 0, 0, 0, 2, 2, 2, 0, 0, 10, 10,
10, 10, 11, 0, 0, 2, 2, 2, 11, 0, 0, 2, 2, 2, 0, 0,
0, 0, 12, 12, 13, 0, 0, 0, 12, 12, 13, 0, 0, 0, 2, 2,
2, 2, 14, 14, 15, 0, 0, 0, 14, 14, 15, 0, 0, 0, 2, 16,
2, 16, 16, 0, 17, 0, 18, 18, 16, 0, 17, 0, 18, 18, 19, 20,
19, 20, 20, 21, 17, 0, 0, 22, 20, 21, 17, 0, 0, 22, 22, 23,
22, 23 0, 0, 0
}; };
static const int deserialize_json_start = 1; static const int deserialize_json_start = 1;
@ -545,22 +560,17 @@ _hb_buffer_deserialize_json (hb_buffer_t *buffer,
/* Ensure we have positions. */ /* Ensure we have positions. */
(void) hb_buffer_get_glyph_positions (buffer, nullptr); (void) hb_buffer_get_glyph_positions (buffer, nullptr);
while (p < pe && ISSPACE (*p))
p++;
if (p < pe && *p == (buffer->len ? ',' : '['))
*end_ptr = ++p;
const char *tok = nullptr; const char *tok = nullptr;
int cs; int cs;
hb_glyph_info_t info = {0}; hb_glyph_info_t info = {0};
hb_glyph_position_t pos = {0}; hb_glyph_position_t pos = {0};
#line 559 "hb-buffer-deserialize-json.hh" #line 569 "hb-buffer-deserialize-json.hh"
{ {
cs = deserialize_json_start; cs = deserialize_json_start;
} }
#line 564 "hb-buffer-deserialize-json.hh" #line 574 "hb-buffer-deserialize-json.hh"
{ {
int _slen; int _slen;
int _trans; int _trans;
@ -772,7 +782,7 @@ _resume:
*end_ptr = p; *end_ptr = p;
} }
break; break;
#line 776 "hb-buffer-deserialize-json.hh" #line 786 "hb-buffer-deserialize-json.hh"
} }
_again: _again:
@ -784,7 +794,7 @@ _again:
_out: {} _out: {}
} }
#line 137 "hb-buffer-deserialize-json.rl" #line 132 "hb-buffer-deserialize-json.rl"
*end_ptr = p; *end_ptr = p;

View File

@ -1983,7 +1983,7 @@ hb_buffer_add_codepoints (hb_buffer_t *buffer,
* @buffer: An #hb_buffer_t * @buffer: An #hb_buffer_t
* @source: source #hb_buffer_t * @source: source #hb_buffer_t
* @start: start index into source buffer to copy. Use 0 to copy from start of buffer. * @start: start index into source buffer to copy. Use 0 to copy from start of buffer.
* @end: end index into source buffer to copy. Use @HB_FEATURE_GLOBAL_END to copy to end of buffer. * @end: end index into source buffer to copy. Use @UINT_MAX (or ((unsigned int) -1)) to copy to end of buffer.
* *
* Append (part of) contents of another buffer to this buffer. * Append (part of) contents of another buffer to this buffer.
* *

View File

@ -32,7 +32,6 @@
#include "hb.hh" #include "hb.hh"
#include "hb-unicode.hh" #include "hb-unicode.hh"
#include "hb-set-digest.hh"
static_assert ((sizeof (hb_glyph_info_t) == 20), ""); static_assert ((sizeof (hb_glyph_info_t) == 20), "");
@ -182,22 +181,24 @@ struct hb_buffer_t
allocated_var_bits = 0; allocated_var_bits = 0;
} }
HB_ALWAYS_INLINE
hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; } hb_glyph_info_t &cur (unsigned int i = 0) { return info[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; } hb_glyph_info_t cur (unsigned int i = 0) const { return info[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; } hb_glyph_position_t &cur_pos (unsigned int i = 0) { return pos[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; } hb_glyph_position_t cur_pos (unsigned int i = 0) const { return pos[idx + i]; }
HB_ALWAYS_INLINE
hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; } hb_glyph_info_t &prev () { return out_info[out_len ? out_len - 1 : 0]; }
HB_ALWAYS_INLINE
hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; } hb_glyph_info_t prev () const { return out_info[out_len ? out_len - 1 : 0]; }
hb_set_digest_t digest () const template <typename set_t>
{ void collect_codepoints (set_t &d) const
hb_set_digest_t d; { d.clear (); d.add_array (&info[0].codepoint, len, sizeof (info[0])); }
d.init ();
d.add_array (&info[0].codepoint, len, sizeof (info[0]));
return d;
}
HB_INTERNAL void similar (const hb_buffer_t &src); HB_INTERNAL void similar (const hb_buffer_t &src);
HB_INTERNAL void reset (); HB_INTERNAL void reset ();

View File

@ -522,7 +522,7 @@ struct parsed_values_t
void alloc (unsigned n) void alloc (unsigned n)
{ {
values.alloc (n, true); values.alloc_exact (n);
} }
void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ()) void add_op (op_code_t op, const byte_str_ref_t& str_ref = byte_str_ref_t (), const VAL &v = VAL ())

View File

@ -157,6 +157,7 @@
#define HB_NO_FALLBACK_SHAPE #define HB_NO_FALLBACK_SHAPE
#define HB_NO_OT_KERN #define HB_NO_OT_KERN
#define HB_NO_OT_LAYOUT_BLOCKLIST #define HB_NO_OT_LAYOUT_BLOCKLIST
#define HB_NO_AAT_LAYOUT_BLOCKLIST
#define HB_NO_OT_SHAPE_FALLBACK #define HB_NO_OT_SHAPE_FALLBACK
#endif #endif

View File

@ -60,11 +60,25 @@ hb_coretext_get_nominal_glyph (hb_font_t *font HB_UNUSED,
void *user_data HB_UNUSED) void *user_data HB_UNUSED)
{ {
CTFontRef ct_font = (CTFontRef) font_data; CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch = unicode; UniChar ch[2];
CGGlyph cg_glyph; CGGlyph cg_glyph[2];
if (CTFontGetGlyphsForCharacters (ct_font, &ch, &cg_glyph, 1)) unsigned count = 0;
if (unicode <= 0xFFFF)
{ {
*glyph = cg_glyph; ch[count++] = unicode;
}
else if (unicode <= 0x10FFFF)
{
ch[count++] = (unicode >> 10) + 0xD7C0;
ch[count++] = (unicode & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
if (CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count))
{
*glyph = cg_glyph[0];
return true; return true;
} }
return false; return false;
@ -80,6 +94,31 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
unsigned int glyph_stride, unsigned int glyph_stride,
void *user_data HB_UNUSED) void *user_data HB_UNUSED)
{ {
// If any non-BMP codepoint is requested, use the slow path.
bool slow_path = false;
auto *unicode = first_unicode;
for (unsigned i = 0; i < count; i++)
{
if (*unicode > 0xFFFF)
{
slow_path = true;
break;
}
unicode = &StructAtOffset<const hb_codepoint_t> (unicode, unicode_stride);
}
if (unlikely (slow_path))
{
for (unsigned i = 0; i < count; i++)
{
if (!hb_coretext_get_nominal_glyph (font, font_data, *first_unicode, first_glyph, nullptr))
return i;
first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
}
return count;
}
CTFontRef ct_font = (CTFontRef) font_data; CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch[MAX_GLYPHS]; UniChar ch[MAX_GLYPHS];
@ -92,7 +131,16 @@ hb_coretext_get_nominal_glyphs (hb_font_t *font HB_UNUSED,
ch[j] = *first_unicode; ch[j] = *first_unicode;
first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride); first_unicode = &StructAtOffset<const hb_codepoint_t> (first_unicode, unicode_stride);
} }
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c); if (unlikely (!CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, c)))
{
// Use slow path partially and return at first failure.
for (unsigned j = 0; j < c; j++)
{
if (!hb_coretext_get_nominal_glyph (font, font_data, ch[j], first_glyph, nullptr))
return i + j;
first_glyph = &StructAtOffset<hb_codepoint_t> (first_glyph, glyph_stride);
}
}
for (unsigned j = 0; j < c; j++) for (unsigned j = 0; j < c; j++)
{ {
*first_glyph = cg_glyph[j]; *first_glyph = cg_glyph[j];
@ -113,12 +161,37 @@ hb_coretext_get_variation_glyph (hb_font_t *font HB_UNUSED,
{ {
CTFontRef ct_font = (CTFontRef) font_data; CTFontRef ct_font = (CTFontRef) font_data;
UniChar ch[2] = { unicode, variation_selector }; UniChar ch[4];
CGGlyph cg_glyph[2]; CGGlyph cg_glyph[4];
unsigned count = 0;
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, 2); // Add Unicode, then variation selector. Ugly, but works.
//
if (unicode <= 0xFFFF)
ch[count++] = unicode;
else if (unicode <= 0x10FFFF)
{
ch[count++] = (unicode >> 10) + 0xD7C0;
ch[count++] = (unicode & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
if (cg_glyph[1]) if (variation_selector <= 0xFFFF)
ch[count++] = variation_selector;
else if (variation_selector <= 0x10FFFF)
{
ch[count++] = (variation_selector >> 10) + 0xD7C0;
ch[count++] = (variation_selector & 0x3FF) + 0xDC00;
}
else
ch[count++] = 0xFFFD;
CTFontGetGlyphsForCharacters (ct_font, ch, cg_glyph, count);
// All except for first should be zero if we succeeded
for (unsigned i = 1; i < count; i++)
if (cg_glyph[i])
return false; return false;
*glyph = cg_glyph[0]; *glyph = cg_glyph[0];
@ -438,10 +511,6 @@ _hb_coretext_get_font_funcs ()
* created with hb_face_create(), and therefore was not * created with hb_face_create(), and therefore was not
* initially configured to use CoreText font functions. * initially configured to use CoreText font functions.
* *
* An #hb_font_t object created with hb_coretext_font_create()
* is preconfigured for CoreText font functions and does not
* require this function to be used.
*
* <note>Note: Internally, this function creates a CTFont. * <note>Note: Internally, this function creates a CTFont.
* </note> * </note>
* *
@ -452,7 +521,12 @@ hb_coretext_font_set_funcs (hb_font_t *font)
{ {
CTFontRef ct_font = hb_coretext_font_get_ct_font (font); CTFontRef ct_font = hb_coretext_font_get_ct_font (font);
if (unlikely (!ct_font)) if (unlikely (!ct_font))
{
hb_font_set_funcs (font,
hb_font_funcs_get_empty (),
nullptr, nullptr);
return; return;
}
hb_font_set_funcs (font, hb_font_set_funcs (font,
_hb_coretext_get_font_funcs (), _hb_coretext_get_font_funcs (),

View File

@ -45,9 +45,6 @@
* Functions for using HarfBuzz with the CoreText fonts. * Functions for using HarfBuzz with the CoreText fonts.
**/ **/
/* https://developer.apple.com/documentation/coretext/1508745-ctfontcreatewithgraphicsfont */
#define HB_CORETEXT_DEFAULT_FONT_SIZE 12.f
static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size); static CTFontRef create_ct_font (CGFontRef cg_font, CGFloat font_size);
static void static void
@ -400,6 +397,7 @@ hb_coretext_face_create_from_file_or_fail (const char *file_name,
return nullptr; return nullptr;
hb_face_t *face = hb_coretext_face_create (cg_font); hb_face_t *face = hb_coretext_face_create (cg_font);
CFRelease (cg_font);
if (unlikely (hb_face_is_immutable (face))) if (unlikely (hb_face_is_immutable (face)))
return nullptr; return nullptr;
@ -432,7 +430,7 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
if (unlikely (!face_data)) return nullptr; if (unlikely (!face_data)) return nullptr;
CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext; CGFontRef cg_font = (CGFontRef) (const void *) face->data.coretext;
CGFloat font_size = (CGFloat) (font->ptem <= 0.f ? HB_CORETEXT_DEFAULT_FONT_SIZE : font->ptem); CGFloat font_size = (CGFloat) (font->ptem > 0.f ? font->ptem : HB_CORETEXT_DEFAULT_FONT_SIZE);
CTFontRef ct_font = create_ct_font (cg_font, font_size); CTFontRef ct_font = create_ct_font (cg_font, font_size);
if (unlikely (!ct_font)) if (unlikely (!ct_font))
@ -451,11 +449,11 @@ _hb_coretext_shaper_font_data_create (hb_font_t *font)
for (unsigned i = 0; i < font->num_coords; i++) for (unsigned i = 0; i < font->num_coords; i++)
{ {
if (font->coords[i] == 0.) continue;
hb_ot_var_axis_info_t info; hb_ot_var_axis_info_t info;
unsigned int c = 1; unsigned int c = 1;
hb_ot_var_get_axis_infos (font->face, i, &c, &info); hb_ot_var_get_axis_infos (font->face, i, &c, &info);
if (font->design_coords[i] == info.default_value)
continue;
float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value); float v = hb_clamp (font->design_coords[i], info.min_value, info.max_value);
CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag); CFNumberRef tag_number = CFNumberCreate (kCFAllocatorDefault, kCFNumberIntType, &info.tag);
@ -499,7 +497,7 @@ _hb_coretext_shaper_font_data_destroy (hb_coretext_font_data_t *data)
* CTFontRef. * CTFontRef.
* *
* The created font uses the default font functions implemented * The created font uses the default font functions implemented
* navitely by HarfBuzz. If you want to use the CoreText font functions * natively by HarfBuzz. If you want to use the CoreText font functions
* instead (rarely needed), you can do so by calling * instead (rarely needed), you can do so by calling
* by hb_coretext_font_set_funcs(). * by hb_coretext_font_set_funcs().
* *
@ -521,6 +519,36 @@ hb_coretext_font_create (CTFontRef ct_font)
hb_font_set_ptem (font, CTFontGetSize (ct_font)); hb_font_set_ptem (font, CTFontGetSize (ct_font));
/* Copy font variations */
CFDictionaryRef variations = CTFontCopyVariation (ct_font);
if (variations)
{
hb_vector_t<hb_variation_t> vars;
hb_vector_t<CFTypeRef> keys;
hb_vector_t<CFTypeRef> values;
CFIndex count = CFDictionaryGetCount (variations);
if (unlikely (!vars.alloc_exact (count) || !keys.resize_exact (count) || !values.resize_exact (count)))
goto done;
// Fetch them one by one and collect in a vector of our own.
CFDictionaryGetKeysAndValues (variations, keys.arrayZ, values.arrayZ);
for (CFIndex i = 0; i < count; i++)
{
int tag;
float value;
CFNumberGetValue ((CFNumberRef) keys.arrayZ[i], kCFNumberIntType, &tag);
CFNumberGetValue ((CFNumberRef) values.arrayZ[i], kCFNumberFloatType, &value);
hb_variation_t var = {tag, value};
vars.push (var);
}
hb_font_set_variations (font, vars.arrayZ, vars.length);
done:
CFRelease (variations);
}
/* Let there be dragons here... */ /* Let there be dragons here... */
font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font)); font->data.coretext.cmpexch (nullptr, (hb_coretext_font_data_t *) CFRetain (ct_font));

View File

@ -0,0 +1,161 @@
/*
* Copyright © 2025 Behdad Esfahbod
*
* This is part of HarfBuzz, a text shaping library.
*
* Permission is hereby granted, without written agreement and without
* license or royalty fees, to use, copy, modify, and distribute this
* software and its documentation for any purpose, provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software.
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
* BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
*
* Author(s): Behdad Esfahbod
*/
#ifndef HB_DECYCLER_HH
#define HB_DECYCLER_HH
#include "hb.hh"
/*
* hb_decycler_t is an efficient cycle detector for graph traversal.
* It's a simple tortoise-and-hare algorithm with a twist: it's
* designed to detect cycles while traversing a graph in a DFS manner,
* instead of just a linked list.
*
* For Floyd's tortoise and hare algorithm, see:
* https://en.wikipedia.org/wiki/Cycle_detection#Floyd's_tortoise_and_hare
*
* Like Floyd's algorithm, hb_decycler_t is O(n) in the number of nodes
* in the graph. Unlike Floyd's algorithm, hb_decycler_t is designed
* to be used in a DFS traversal, where the graph is not a simple
* linked list, but a tree with cycles. Like Floyd's algorithm, it is
* constant-memory (just two pointers).
*
* The decycler works by creating an implicit linked-list on the stack,
* of the path from the root to the current node, and apply Floyd's
* algorithm on that list as it goes.
*
* The decycler is malloc-free, and as such, much faster to use than a
* hb_set_t or hb_map_t equivalent.
*
* The decycler detects cycles in the graph *eventually*, not *immediately*.
* That is, it may not detect a cycle until the cycle is fully traversed,
* even multiple times. See Floyd's algorithm analysis for details.
*
* The implementation saves a pointer storage on the stack by combining
* this->u.decycler and this->u.next into a union. This is possible because
* at any point we only need one of those values. The invariant is that
* after construction, and before destruction, of a node, the u.decycler
* field is always valid. The u.next field is only valid when the node is
* in the traversal path, parent to another node.
*
* There are three method's:
*
* - hb_decycler_node_t() constructor: Creates a new node in the traversal.
* The constructor takes a reference to the decycler object and inserts
* itself as the latest node in the traversal path, by advancing the hare
* pointer, and for every other descent, advancing the tortoise pointer.
*
* - ~hb_decycler_node_t() destructor: Restores the decycler object to its
* previous state by removing the node from the traversal path.
*
* - bool visit(uintptr_t value): Called on every node in the graph. Returns
* true if the node is not part of a cycle, and false if it is. The value
* parameter is used to detect cycles. It's the caller's responsibility
* to ensure that the value is unique for each node in the graph.
* The cycle detection is as simple as comparing the value to the value
* held by the tortoise pointer, which is the Floyd's algorithm.
*
* For usage examples see test-decycler.cc.
*/
struct hb_decycler_node_t;
struct hb_decycler_t
{
friend struct hb_decycler_node_t;
private:
bool tortoise_asleep = true;
hb_decycler_node_t *tortoise = nullptr;
hb_decycler_node_t *hare = nullptr;
};
struct hb_decycler_node_t
{
hb_decycler_node_t (hb_decycler_t &decycler)
{
u.decycler = &decycler;
decycler.tortoise_asleep = !decycler.tortoise_asleep;
if (!decycler.tortoise)
{
// First node.
decycler.tortoise = decycler.hare = this;
return;
}
if (!decycler.tortoise_asleep)
decycler.tortoise = decycler.tortoise->u.next; // Time to move.
this->prev = decycler.hare;
decycler.hare->u.next = this;
decycler.hare = this;
}
~hb_decycler_node_t ()
{
hb_decycler_t &decycler = *u.decycler;
// Inverse of the constructor.
assert (decycler.hare == this);
decycler.hare = prev;
if (prev)
prev->u.decycler = &decycler;
assert (decycler.tortoise);
if (!decycler.tortoise_asleep)
decycler.tortoise = decycler.tortoise->prev;
decycler.tortoise_asleep = !decycler.tortoise_asleep;
}
bool visit (uintptr_t value_)
{
value = value_;
hb_decycler_t &decycler = *u.decycler;
if (decycler.tortoise == this)
return true; // First node; not a cycle.
if (decycler.tortoise->value == value)
return false; // Cycle detected.
return true;
}
private:
union {
hb_decycler_t *decycler;
hb_decycler_node_t *next;
} u = {nullptr};
hb_decycler_node_t *prev = nullptr;
uintptr_t value = 0;
};
#endif /* HB_DECYCLER_HH */

View File

@ -29,6 +29,7 @@
#include "hb-shaper-impl.hh" #include "hb-shaper-impl.hh"
#include <dwrite_1.h> #include <dwrite_1.h>
#include <dwrite_3.h>
#include "hb-directwrite.h" #include "hb-directwrite.h"
@ -275,6 +276,8 @@ _hb_directwrite_shaper_font_data_create (hb_font_t *font)
void void
_hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data) _hb_directwrite_shaper_font_data_destroy (hb_directwrite_font_data_t *data)
{ {
if (data != HB_SHAPER_DATA_SUCCEEDED)
((IDWriteFont *) data)->Release();
} }
@ -839,7 +842,7 @@ _hb_directwrite_reference_table (hb_face_t *face HB_UNUSED, hb_tag_t tag, void *
} }
static void static void
_hb_directwrite_font_release (void *data) _hb_directwrite_face_release (void *data)
{ {
if (data) if (data)
((IDWriteFontFace *) data)->Release (); ((IDWriteFontFace *) data)->Release ();
@ -847,7 +850,7 @@ _hb_directwrite_font_release (void *data)
/** /**
* hb_directwrite_face_create: * hb_directwrite_face_create:
* @font_face: a DirectWrite IDWriteFontFace object. * @dw_face: a DirectWrite IDWriteFontFace object.
* *
* Constructs a new face object from the specified DirectWrite IDWriteFontFace. * Constructs a new face object from the specified DirectWrite IDWriteFontFace.
* *
@ -856,12 +859,12 @@ _hb_directwrite_font_release (void *data)
* Since: 2.4.0 * Since: 2.4.0
**/ **/
hb_face_t * hb_face_t *
hb_directwrite_face_create (IDWriteFontFace *font_face) hb_directwrite_face_create (IDWriteFontFace *dw_face)
{ {
if (font_face) if (dw_face)
font_face->AddRef (); dw_face->AddRef ();
return hb_face_create_for_tables (_hb_directwrite_reference_table, font_face, return hb_face_create_for_tables (_hb_directwrite_reference_table, dw_face,
_hb_directwrite_font_release); _hb_directwrite_face_release);
} }
/** /**
@ -880,5 +883,80 @@ hb_directwrite_face_get_font_face (hb_face_t *face)
return face->data.directwrite->fontFace; return face->data.directwrite->fontFace;
} }
/**
* hb_directwrite_font_create:
* @dw_font: a DirectWrite IDWriteFont object.
*
* Constructs a new font object from the specified DirectWrite IDWriteFont.
*
* Return value: #hb_font_t object corresponding to the given input
*
* Since: 10.3.0
**/
hb_font_t *
hb_directwrite_font_create (IDWriteFont *dw_font)
{
IDWriteFontFace *dw_face = nullptr;
IDWriteFontFace5 *dw_face5 = nullptr;
if (FAILED (dw_font->CreateFontFace (&dw_face)))
return hb_font_get_empty ();
hb_face_t *face = hb_directwrite_face_create (dw_face);
hb_font_t *font = hb_font_create (face);
hb_face_destroy (face);
if (unlikely (hb_object_is_immutable (font)))
goto done;
/* Copy font variations */
if (SUCCEEDED (dw_face->QueryInterface (__uuidof (IDWriteFontFace5), (void**) &dw_face5)))
{
if (dw_face5->HasVariations ())
{
hb_vector_t<DWRITE_FONT_AXIS_VALUE> values;
uint32_t count = dw_face5->GetFontAxisValueCount ();
if (likely (values.resize_exact (count)) &&
SUCCEEDED (dw_face5->GetFontAxisValues (values.arrayZ, count)))
{
hb_vector_t<hb_variation_t> vars;
if (likely (vars.resize_exact (count)))
{
for (uint32_t i = 0; i < count; ++i)
{
hb_tag_t tag = values[i].axisTag;
float value = values[i].value;
vars[i] = {tag, value};
}
hb_font_set_variations (font, vars.arrayZ, vars.length);
}
}
}
dw_face5->Release ();
}
dw_font->AddRef ();
font->data.directwrite.cmpexch (nullptr, (hb_directwrite_font_data_t *) dw_font);
done:
dw_face->Release ();
return font;
}
/**
* hb_directwrite_font_get_dw_font:
* @font: a #hb_font_t object
*
* Gets the DirectWrite IDWriteFont associated with @font.
*
* Return value: DirectWrite IDWriteFont object corresponding to the given input
*
* Since: 10.3.0
**/
IDWriteFont *
hb_directwrite_font_get_dw_font (hb_font_t *font)
{
return (IDWriteFont *) (const void *) font->data.directwrite;
}
#endif #endif

View File

@ -30,11 +30,17 @@
HB_BEGIN_DECLS HB_BEGIN_DECLS
HB_EXTERN hb_face_t * HB_EXTERN hb_face_t *
hb_directwrite_face_create (IDWriteFontFace *font_face); hb_directwrite_face_create (IDWriteFontFace *dw_face);
HB_EXTERN IDWriteFontFace * HB_EXTERN IDWriteFontFace *
hb_directwrite_face_get_font_face (hb_face_t *face); hb_directwrite_face_get_font_face (hb_face_t *face);
HB_EXTERN hb_font_t *
hb_directwrite_font_create (IDWriteFont *dw_font);
HB_EXTERN IDWriteFont *
hb_directwrite_font_get_dw_font (hb_font_t *font);
HB_END_DECLS HB_END_DECLS
#endif /* HB_DIRECTWRITE_H */ #endif /* HB_DIRECTWRITE_H */

View File

@ -291,6 +291,7 @@ hb_face_create_or_fail (hb_blob_t *blob,
return face; return face;
} }
#ifndef HB_NO_OPEN
/** /**
* hb_face_create_from_file_or_fail: * hb_face_create_from_file_or_fail:
* @file_name: A font filename * @file_name: A font filename
@ -317,6 +318,7 @@ hb_face_create_from_file_or_fail (const char *file_name,
return face; return face;
} }
#endif
/** /**
* hb_face_get_empty: * hb_face_get_empty:
@ -491,9 +493,10 @@ hb_face_reference_table (const hb_face_t *face,
* hb_face_reference_blob: * hb_face_reference_blob:
* @face: A face object * @face: A face object
* *
* Fetches a pointer to the binary blob that contains the * Fetches a pointer to the binary blob that contains the specified face.
* specified face. Returns an empty blob if referencing face data is not * If referencing the face data is not possible, this function creates a blob
* possible. * out of individual table blobs if hb_face_get_table_tags() works with this
* face, otherwise it returns an empty blob.
* *
* Return value: (transfer full): A pointer to the blob for @face * Return value: (transfer full): A pointer to the blob for @face
* *
@ -502,7 +505,37 @@ hb_face_reference_table (const hb_face_t *face,
hb_blob_t * hb_blob_t *
hb_face_reference_blob (hb_face_t *face) hb_face_reference_blob (hb_face_t *face)
{ {
return face->reference_table (HB_TAG_NONE); hb_blob_t *blob = face->reference_table (HB_TAG_NONE);
if (blob == hb_blob_get_empty ())
{
// If referencing the face blob is not possible (e.g. not implemented by the
// font functions), use face builder to create a blob out of individual
// table blobs.
unsigned total_count = hb_face_get_table_tags (face, 0, nullptr, nullptr);
if (total_count)
{
hb_tag_t tags[64];
unsigned count = ARRAY_LENGTH (tags);
hb_face_t* builder = hb_face_builder_create ();
for (unsigned offset = 0; offset < total_count; offset += count)
{
hb_face_get_table_tags (face, offset, &count, tags);
for (unsigned i = 0; i < count; i++)
{
hb_blob_t *table = hb_face_reference_table (face, tags[i]);
hb_face_builder_add_table (builder, tags[i], table);
hb_blob_destroy (table);
}
}
blob = hb_face_reference_blob (builder);
hb_face_destroy (builder);
}
}
return blob;
} }
/** /**
@ -644,6 +677,7 @@ hb_face_set_get_table_tags_func (hb_face_t *face,
{ {
if (destroy) if (destroy)
destroy (user_data); destroy (user_data);
return;
} }
if (face->get_table_tags_destroy) if (face->get_table_tags_destroy)

View File

@ -73,9 +73,14 @@ hb_face_create_from_file_or_fail (const char *file_name,
* @tag: the tag of the table to reference * @tag: the tag of the table to reference
* @user_data: User data pointer passed by the caller * @user_data: User data pointer passed by the caller
* *
* Callback function for hb_face_create_for_tables(). * Callback function for hb_face_create_for_tables(). The @tag is the tag of the
* table to reference, and the special tag #HB_TAG_NONE is used to reference the
* blob of the face itself. If referencing the face blob is not possible, it is
* recommended to set hb_get_table_tags_func_t on the @face to allow
* hb_face_reference_blob() to create a face blob out of individual table blobs.
* *
* Return value: (transfer full): A pointer to the @tag table within @face * Return value: (transfer full): A pointer to the @tag table within @face or
* `NULL` if the table is not found or cannot be referenced.
* *
* Since: 0.9.2 * Since: 0.9.2
*/ */

View File

@ -27,6 +27,7 @@
#include "hb.hh" #include "hb.hh"
#include "hb-decycler.hh"
#include "hb-paint-extents.hh" #include "hb-paint-extents.hh"
#include FT_COLOR_H #include FT_COLOR_H
@ -105,8 +106,8 @@ struct hb_ft_paint_context_t
FT_Color *palette; FT_Color *palette;
unsigned palette_index; unsigned palette_index;
hb_color_t foreground; hb_color_t foreground;
hb_map_t current_glyphs; hb_decycler_t glyphs_decycler;
hb_map_t current_layers; hb_decycler_t layers_decycler;
int depth_left = HB_MAX_NESTING_LEVEL; int depth_left = HB_MAX_NESTING_LEVEL;
int edge_count = HB_MAX_GRAPH_EDGE_COUNT; int edge_count = HB_MAX_GRAPH_EDGE_COUNT;
}; };
@ -218,22 +219,19 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
case FT_COLR_PAINTFORMAT_COLR_LAYERS: case FT_COLR_PAINTFORMAT_COLR_LAYERS:
{ {
FT_OpaquePaint other_paint = {0}; FT_OpaquePaint other_paint = {0};
hb_decycler_node_t node (c->layers_decycler);
while (FT_Get_Paint_Layers (ft_face, while (FT_Get_Paint_Layers (ft_face,
&paint.u.colr_layers.layer_iterator, &paint.u.colr_layers.layer_iterator,
&other_paint)) &other_paint))
{ {
unsigned i = paint.u.colr_layers.layer_iterator.layer; // FreeType doesn't provide a way to get the layer index, so we use the pointer
// for cycle detection.
if (unlikely (c->current_layers.has (i))) if (unlikely (!node.visit ((uintptr_t) other_paint.p)))
continue; continue;
c->current_layers.add (i);
c->funcs->push_group (c->data); c->funcs->push_group (c->data);
c->recurse (other_paint); c->recurse (other_paint);
c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER); c->funcs->pop_group (c->data, HB_PAINT_COMPOSITE_MODE_SRC_OVER);
c->current_layers.del (i);
} }
} }
break; break;
@ -333,18 +331,16 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
{ {
hb_codepoint_t gid = paint.u.colr_glyph.glyphID; hb_codepoint_t gid = paint.u.colr_glyph.glyphID;
if (unlikely (c->current_glyphs.has (gid))) hb_decycler_node_t node (c->glyphs_decycler);
if (unlikely (!node.visit (gid)))
return; return;
c->current_glyphs.add (gid);
c->funcs->push_inverse_root_transform (c->data, c->font); c->funcs->push_inverse_root_transform (c->data, c->font);
c->ft_font->lock.unlock (); c->ft_font->lock.unlock ();
if (c->funcs->color_glyph (c->data, gid, c->font)) if (c->funcs->color_glyph (c->data, gid, c->font))
{ {
c->ft_font->lock.lock (); c->ft_font->lock.lock ();
c->funcs->pop_transform (c->data); c->funcs->pop_transform (c->data);
c->current_glyphs.del (gid);
return; return;
} }
c->ft_font->lock.lock (); c->ft_font->lock.lock ();
@ -380,8 +376,6 @@ _hb_ft_paint (hb_ft_paint_context_t *c,
if (has_clip_box) if (has_clip_box)
c->funcs->pop_clip (c->data); c->funcs->pop_clip (c->data);
c->current_glyphs.del (gid);
} }
} }
break; break;
@ -506,7 +500,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
hb_ft_paint_context_t c (ft_font, font, hb_ft_paint_context_t c (ft_font, font,
paint_funcs, paint_data, paint_funcs, paint_data,
palette, palette_index, foreground); palette, palette_index, foreground);
c.current_glyphs.add (gid); hb_decycler_node_t node (c.glyphs_decycler);
node.visit (gid);
bool is_bounded = true; bool is_bounded = true;
FT_ClipBox clip_box; FT_ClipBox clip_box;
@ -530,7 +525,8 @@ hb_ft_paint_glyph_colr (hb_font_t *font,
hb_ft_paint_context_t ce (ft_font, font, hb_ft_paint_context_t ce (ft_font, font,
extents_funcs, &extents_data, extents_funcs, &extents_data,
palette, palette_index, foreground); palette, palette_index, foreground);
ce.current_glyphs.add (gid); hb_decycler_node_t node2 (ce.glyphs_decycler);
node2.visit (gid);
ce.funcs->push_root_transform (ce.data, font); ce.funcs->push_root_transform (ce.data, font);
ce.recurse (paint); ce.recurse (paint);
ce.funcs->pop_transform (ce.data); ce.funcs->pop_transform (ce.data);

View File

@ -37,7 +37,11 @@
#include "hb-draw.hh" #include "hb-draw.hh"
#include "hb-font.hh" #include "hb-font.hh"
#include "hb-machinery.hh" #include "hb-machinery.hh"
#ifndef HB_NO_AAT
#include "hb-aat-layout-trak-table.hh"
#endif
#include "hb-ot-os2-table.hh" #include "hb-ot-os2-table.hh"
#include "hb-ot-stat-table.hh"
#include "hb-ot-shaper-arabic-pua.hh" #include "hb-ot-shaper-arabic-pua.hh"
#include "hb-paint.hh" #include "hb-paint.hh"
@ -502,6 +506,26 @@ hb_ft_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
bool apply_trak = false;
#endif
if (apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
} }
#ifndef HB_NO_VERTICAL #ifndef HB_NO_VERTICAL
@ -538,7 +562,20 @@ hb_ft_get_glyph_v_advance (hb_font_t *font,
* have a Y growing upward. Hence the extra negation. */ * have a Y growing upward. Hence the extra negation. */
hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength; hb_position_t y_strength = font->y_scale >= 0 ? font->y_strength : -font->y_strength;
return ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength); v = ((-v + (1<<9)) >> 10) + (font->embolden_in_place ? 0 : y_strength);
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
bool apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
bool apply_trak = false;
#endif
if (apply_trak)
v += font->face->table.trak->get_v_tracking (font);
#endif
return v;
} }
#endif #endif
@ -1290,7 +1327,7 @@ hb_ft_face_create_cached (FT_Face ft_face)
* *
* If you know you have valid reasons not to use hb_ft_font_create_referenced(), * If you know you have valid reasons not to use hb_ft_font_create_referenced(),
* then it is the client program's responsibility to destroy @ft_face * then it is the client program's responsibility to destroy @ft_face
* after the #hb_font_t font object has been destroyed. * only after the #hb_font_t font object has been destroyed.
* *
* HarfBuzz will use the @destroy callback on the #hb_font_t font object * HarfBuzz will use the @destroy callback on the #hb_font_t font object
* if it is supplied when you use this function. However, even if @destroy * if it is supplied when you use this function. However, even if @destroy
@ -1598,6 +1635,11 @@ _release_blob (void *arg)
void void
hb_ft_font_set_funcs (hb_font_t *font) hb_ft_font_set_funcs (hb_font_t *font)
{ {
// In case of failure...
hb_font_set_funcs (font,
hb_font_funcs_get_empty (),
nullptr, nullptr);
hb_blob_t *blob = hb_face_reference_blob (font->face); hb_blob_t *blob = hb_face_reference_blob (font->face);
unsigned int blob_length; unsigned int blob_length;
const char *blob_data = hb_blob_get_data (blob, &blob_length); const char *blob_data = hb_blob_get_data (blob, &blob_length);

View File

@ -83,6 +83,13 @@ struct hb_transform_t
float x0, float y0) : float x0, float y0) :
xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {} xx (xx), yx (yx), xy (xy), yy (yy), x0 (x0), y0 (y0) {}
bool is_identity () const
{
return xx == 1.f && yx == 0.f &&
xy == 0.f && yy == 1.f &&
x0 == 0.f && y0 == 0.f;
}
void multiply (const hb_transform_t &o) void multiply (const hb_transform_t &o)
{ {
/* Copied from cairo, with "o" being "a" there and "this" being "b" there. */ /* Copied from cairo, with "o" being "a" there and "this" being "b" there. */
@ -201,6 +208,8 @@ struct hb_transform_t
float y0 = 0.f; float y0 = 0.f;
}; };
#define HB_TRANSFORM_IDENTITY hb_transform_t{1.f, 0.f, 0.f, 1.f, 0.f, 0.f}
struct hb_bounds_t struct hb_bounds_t
{ {
enum status_t { enum status_t {

View File

@ -86,21 +86,12 @@ struct IntType
return pb->cmp (*pa); return pb->cmp (*pa);
} }
template <typename Type2,
hb_enable_if (std::is_integral<Type2>::value &&
sizeof (Type2) < sizeof (int) &&
sizeof (Type) < sizeof (int))>
int cmp (Type2 a) const
{
Type b = v;
return (int) a - (int) b;
}
template <typename Type2, template <typename Type2,
hb_enable_if (hb_is_convertible (Type2, Type))> hb_enable_if (hb_is_convertible (Type2, Type))>
int cmp (Type2 a) const int cmp (Type2 a) const
{ {
Type b = v; Type b = v;
return a < b ? -1 : a == b ? 0 : +1; return (a > b) - (a < b);
} }
bool sanitize (hb_sanitize_context_t *c) const bool sanitize (hb_sanitize_context_t *c) const
{ {
@ -299,11 +290,6 @@ typedef Index NameID;
struct VarIdx : HBUINT32 { struct VarIdx : HBUINT32 {
static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu; static constexpr unsigned NO_VARIATION = 0xFFFFFFFFu;
static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, ""); static_assert (NO_VARIATION == HB_OT_LAYOUT_NO_VARIATIONS_INDEX, "");
static uint32_t add (uint32_t i, unsigned short v)
{
if (i == NO_VARIATION) return i;
return i + v;
}
VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; } VarIdx& operator = (uint32_t i) { HBUINT32::operator= (i); return *this; }
}; };
DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx); DECLARE_NULL_NAMESPACE_BYTES (OT, VarIdx);

View File

@ -553,15 +553,6 @@ bool _get_path (const OT::cff1::accelerator_t *cff, hb_font_t *font, hb_codepoin
return true; return true;
} }
bool OT::cff1::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, glyph, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const bool OT::cff1::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const
{ {
#ifdef HB_NO_OT_FONT_CFF #ifdef HB_NO_OT_FONT_CFF

View File

@ -1462,7 +1462,6 @@ struct cff1
} }
HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const; HB_INTERNAL bool get_extents (hb_font_t *font, hb_codepoint_t glyph, hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
private: private:

View File

@ -143,15 +143,6 @@ bool OT::cff2::accelerator_t::get_extents (hb_font_t *font,
return true; return true;
} }
bool OT::cff2::accelerator_t::paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const
{
funcs->push_clip_glyph (data, glyph, font);
funcs->color (data, true, foreground);
funcs->pop_clip (data);
return true;
}
struct cff2_path_param_t struct cff2_path_param_t
{ {
cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_) cff2_path_param_t (hb_font_t *font_, hb_draw_session_t &draw_session_)

View File

@ -518,7 +518,6 @@ struct cff2
HB_INTERNAL bool get_extents (hb_font_t *font, HB_INTERNAL bool get_extents (hb_font_t *font,
hb_codepoint_t glyph, hb_codepoint_t glyph,
hb_glyph_extents_t *extents) const; hb_glyph_extents_t *extents) const;
HB_INTERNAL bool paint_glyph (hb_font_t *font, hb_codepoint_t glyph, hb_paint_funcs_t *funcs, void *data, hb_color_t foreground) const;
HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const; HB_INTERNAL bool get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const;
HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const; HB_INTERNAL bool get_path_at (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session, hb_array_t<const int> coords) const;
}; };

View File

@ -36,13 +36,16 @@
#include "hb-ot-face.hh" #include "hb-ot-face.hh"
#include "hb-outline.hh" #include "hb-outline.hh"
#ifndef HB_NO_AAT
#include "hb-aat-layout-trak-table.hh"
#endif
#include "hb-ot-cmap-table.hh" #include "hb-ot-cmap-table.hh"
#include "hb-ot-glyf-table.hh" #include "hb-ot-glyf-table.hh"
#include "hb-ot-cff2-table.hh" #include "hb-ot-cff2-table.hh"
#include "hb-ot-cff1-table.hh" #include "hb-ot-cff1-table.hh"
#include "hb-ot-hmtx-table.hh" #include "hb-ot-hmtx-table.hh"
#include "hb-ot-post-table.hh" #include "hb-ot-post-table.hh"
#include "hb-ot-stat-table.hh" // Just so we compile it; unused otherwise. #include "hb-ot-stat-table.hh"
#include "hb-ot-var-varc-table.hh" #include "hb-ot-var-varc-table.hh"
#include "hb-ot-vorg-table.hh" #include "hb-ot-vorg-table.hh"
#include "OT/Color/CBDT/CBDT.hh" #include "OT/Color/CBDT/CBDT.hh"
@ -73,6 +76,10 @@ struct hb_ot_font_t
{ {
const hb_ot_face_t *ot_face; const hb_ot_face_t *ot_face;
#ifndef HB_NO_AAT
bool apply_trak;
#endif
#ifndef HB_NO_OT_FONT_CMAP_CACHE #ifndef HB_NO_OT_FONT_CMAP_CACHE
hb_ot_font_cmap_cache_t *cmap_cache; hb_ot_font_cmap_cache_t *cmap_cache;
#endif #endif
@ -91,6 +98,15 @@ _hb_ot_font_create (hb_font_t *font)
ot_font->ot_face = &font->face->table; ot_font->ot_face = &font->face->table;
#ifndef HB_NO_AAT
/* According to Ned, trak is applied by default for "modern fonts", as detected by presence of STAT table. */
#ifndef HB_NO_STYLE
ot_font->apply_trak = font->face->table.STAT->has_data () && font->face->table.trak->has_data ();
#else
ot_font->apply_trak = false;
#endif
#endif
#ifndef HB_NO_OT_FONT_CMAP_CACHE #ifndef HB_NO_OT_FONT_CMAP_CACHE
// retry: // retry:
auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face, auto *cmap_cache = (hb_ot_font_cmap_cache_t *) hb_face_get_user_data (font->face,
@ -200,7 +216,6 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
unsigned advance_stride, unsigned advance_stride,
void *user_data HB_UNUSED) void *user_data HB_UNUSED)
{ {
const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data; const hb_ot_font_t *ot_font = (const hb_ot_font_t *) font_data;
const hb_ot_face_t *ot_face = ot_font->ot_face; const hb_ot_face_t *ot_face = ot_font->ot_face;
const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx; const OT::hmtx_accelerator_t &hmtx = *ot_face->hmtx;
@ -292,6 +307,20 @@ hb_ot_get_glyph_h_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
#ifndef HB_NO_AAT
if (ot_font->apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_h_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
} }
#ifndef HB_NO_VERTICAL #ifndef HB_NO_VERTICAL
@ -356,6 +385,20 @@ hb_ot_get_glyph_v_advances (hb_font_t* font, void* font_data,
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride); first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
} }
} }
#ifndef HB_NO_AAT
if (ot_font->apply_trak)
{
hb_position_t tracking = font->face->table.trak->get_v_tracking (font);
first_advance = orig_first_advance;
for (unsigned int i = 0; i < count; i++)
{
*first_advance += tracking;
first_glyph = &StructAtOffsetUnaligned<hb_codepoint_t> (first_glyph, glyph_stride);
first_advance = &StructAtOffsetUnaligned<hb_position_t> (first_advance, advance_stride);
}
}
#endif
} }
#endif #endif
@ -568,14 +611,11 @@ hb_ot_paint_glyph (hb_font_t *font,
if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return; if (font->face->table.sbix->paint_glyph (font, glyph, paint_funcs, paint_data)) return;
#endif #endif
#endif #endif
#ifndef HB_NO_VAR_COMPOSITES
if (font->face->table.VARC->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; // Outline glyph
#endif paint_funcs->push_clip_glyph (paint_data, glyph, font);
if (font->face->table.glyf->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return; paint_funcs->color (paint_data, true, foreground);
#ifndef HB_NO_CFF paint_funcs->pop_clip (paint_data);
if (font->face->table.cff2->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
if (font->face->table.cff1->paint_glyph (font, glyph, paint_funcs, paint_data, foreground)) return;
#endif
} }
#endif #endif

View File

@ -342,7 +342,7 @@ struct kern
} }
bool apply (AAT::hb_aat_apply_context_t *c, bool apply (AAT::hb_aat_apply_context_t *c,
const AAT::kern_accelerator_data_t *accel_data = nullptr) const const AAT::kern_accelerator_data_t &accel_data) const
{ return dispatch (c, accel_data); } { return dispatch (c, accel_data); }
template <typename context_t, typename ...Ts> template <typename context_t, typename ...Ts>
@ -395,7 +395,7 @@ struct kern
bool apply (AAT::hb_aat_apply_context_t *c) const bool apply (AAT::hb_aat_apply_context_t *c) const
{ {
return table->apply (c, &accel_data); return table->apply (c, accel_data);
} }
hb_blob_ptr_t<kern> table; hb_blob_ptr_t<kern> table;

View File

@ -34,6 +34,7 @@
#include "hb-open-type.hh" #include "hb-open-type.hh"
#include "hb-set.hh" #include "hb-set.hh"
#include "hb-bimap.hh" #include "hb-bimap.hh"
#include "hb-cache.hh"
#include "OT/Layout/Common/Coverage.hh" #include "OT/Layout/Common/Coverage.hh"
#include "OT/Layout/types.hh" #include "OT/Layout/types.hh"
@ -2076,6 +2077,15 @@ struct ClassDef
default:return 0; default:return 0;
} }
} }
unsigned int get_class (hb_codepoint_t glyph_id,
hb_ot_lookup_cache_t *cache) const
{
unsigned klass;
if (cache && cache->get (glyph_id, &klass)) return klass;
klass = get_class (glyph_id);
if (cache) cache->set (glyph_id, klass);
return klass;
}
unsigned get_population () const unsigned get_population () const
{ {
@ -3731,11 +3741,13 @@ struct ItemVarStoreInstancer
float operator() (uint32_t varIdx, unsigned short offset = 0) const float operator() (uint32_t varIdx, unsigned short offset = 0) const
{ {
if (varIdxMap) if (!coords || varIdx == VarIdx::NO_VARIATION)
varIdx = varIdxMap->map (VarIdx::add (varIdx, offset)); return 0.f;
else
varIdx += offset; varIdx += offset;
return coords ? varStore->get_delta (varIdx, coords, cache) : 0.f; if (varIdxMap)
varIdx = varIdxMap->map (varIdx);
return varStore->get_delta (varIdx, coords, cache);
} }
const ItemVariationStore *varStore; const ItemVariationStore *varStore;
@ -3767,12 +3779,11 @@ struct MultiItemVarStoreInstancer
void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
{ {
if (coords) if (coords && varIdx != VarIdx::NO_VARIATION)
{ {
if (varIdxMap)
varIdx = varIdxMap->map (VarIdx::add (varIdx, offset));
else
varIdx += offset; varIdx += offset;
if (varIdxMap)
varIdx = varIdxMap->map (varIdx);
varStore->get_delta (varIdx, coords, out, cache); varStore->get_delta (varIdx, coords, out, cache);
} }
else else
@ -3890,8 +3901,8 @@ struct ConditionAxisRange
{ {
// add axisIndex->value into the hashmap so we can check if the record is // add axisIndex->value into the hashmap so we can check if the record is
// unique with variations // unique with variations
int16_t int_filter_max_val = filterRangeMaxValue.to_int (); uint16_t int_filter_max_val = (uint16_t) filterRangeMaxValue.to_int ();
int16_t int_filter_min_val = filterRangeMinValue.to_int (); uint16_t int_filter_min_val = (uint16_t) filterRangeMinValue.to_int ();
hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val; hb_codepoint_t val = (int_filter_max_val << 16) + int_filter_min_val;
condition_map->set (axisIndex, val); condition_map->set (axisIndex, val);

View File

@ -713,6 +713,7 @@ struct hb_ot_apply_context_t :
recurse_func_t recurse_func = nullptr; recurse_func_t recurse_func = nullptr;
const GDEF &gdef; const GDEF &gdef;
const GDEF::accelerator_t &gdef_accel; const GDEF::accelerator_t &gdef_accel;
const hb_ot_layout_lookup_accelerator_t *lookup_accel = nullptr;
const ItemVariationStore &var_store; const ItemVariationStore &var_store;
ItemVariationStore::cache_t *var_store_cache; ItemVariationStore::cache_t *var_store_cache;
hb_set_digest_t digest; hb_set_digest_t digest;
@ -762,10 +763,12 @@ struct hb_ot_apply_context_t :
nullptr nullptr
#endif #endif
), ),
digest (buffer_->digest ()),
direction (buffer_->props.direction), direction (buffer_->props.direction),
has_glyph_classes (gdef.has_glyph_classes ()) has_glyph_classes (gdef.has_glyph_classes ())
{ init_iters (); } {
init_iters ();
buffer->collect_codepoints (digest);
}
~hb_ot_apply_context_t () ~hb_ot_apply_context_t ()
{ {
@ -899,6 +902,13 @@ struct hb_ot_apply_context_t :
} }
}; };
enum class hb_ot_lookup_cache_op_t
{
CREATE,
ENTER,
LEAVE,
DESTROY,
};
struct hb_accelerate_subtables_context_t : struct hb_accelerate_subtables_context_t :
hb_dispatch_context_t<hb_accelerate_subtables_context_t> hb_dispatch_context_t<hb_accelerate_subtables_context_t>
@ -923,19 +933,23 @@ struct hb_accelerate_subtables_context_t :
} }
template <typename T> template <typename T>
static inline auto cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<1>) HB_RETURN (bool, obj->cache_func (c, enter) ) static inline auto cache_func_ (void *p,
template <typename T> hb_ot_lookup_cache_op_t op,
static inline bool cache_func_ (const T *obj, hb_ot_apply_context_t *c, bool enter, hb_priority<0>) { return false; } hb_priority<1>) HB_RETURN (void *, T::cache_func (p, op) )
template <typename T=void>
static inline void * cache_func_ (void *p,
hb_ot_lookup_cache_op_t op HB_UNUSED,
hb_priority<0>) { return (void *) false; }
template <typename Type> template <typename Type>
static inline bool cache_func_to (const void *obj, hb_ot_apply_context_t *c, bool enter) static inline void * cache_func_to (void *p,
hb_ot_lookup_cache_op_t op)
{ {
const Type *typed_obj = (const Type *) obj; return cache_func_<Type> (p, op, hb_prioritize);
return cache_func_ (typed_obj, c, enter, hb_prioritize);
} }
#endif #endif
typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c); typedef bool (*hb_apply_func_t) (const void *obj, hb_ot_apply_context_t *c);
typedef bool (*hb_cache_func_t) (const void *obj, hb_ot_apply_context_t *c, bool enter); typedef void * (*hb_cache_func_t) (void *p, hb_ot_lookup_cache_op_t op);
struct hb_applicable_t struct hb_applicable_t
{ {
@ -972,11 +986,11 @@ struct hb_accelerate_subtables_context_t :
} }
bool cache_enter (hb_ot_apply_context_t *c) const bool cache_enter (hb_ot_apply_context_t *c) const
{ {
return cache_func (obj, c, true); return (bool) cache_func (c, hb_ot_lookup_cache_op_t::ENTER);
} }
void cache_leave (hb_ot_apply_context_t *c) const void cache_leave (hb_ot_apply_context_t *c) const
{ {
cache_func (obj, c, false); cache_func (c, hb_ot_lookup_cache_op_t::LEAVE);
} }
#endif #endif
@ -2623,25 +2637,35 @@ struct ContextFormat2_5
unsigned c = (this+classDef).cost () * ruleSet.len; unsigned c = (this+classDef).cost () * ruleSet.len;
return c >= 4 ? c : 0; return c >= 4 ? c : 0;
} }
bool cache_func (hb_ot_apply_context_t *c, bool enter) const static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{ {
if (enter) switch (op)
{ {
case hb_ot_lookup_cache_op_t::CREATE:
return (void *) true;
case hb_ot_lookup_cache_op_t::ENTER:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return false; return (void *) false;
auto &info = c->buffer->info; auto &info = c->buffer->info;
unsigned count = c->buffer->len; unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255; info[i].syllable() = 255;
c->new_syllables = 255; c->new_syllables = 255;
return true; return (void *) true;
} }
else case hb_ot_lookup_cache_op_t::LEAVE:
{ {
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
c->new_syllables = (unsigned) -1; c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return true; return nullptr;
} }
case hb_ot_lookup_cache_op_t::DESTROY:
return nullptr;
}
return nullptr;
} }
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@ -2650,7 +2674,7 @@ struct ContextFormat2_5
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
const ClassDef &class_def = this+classDef; const ClassDef &class_def = this+classDef;
@ -2836,7 +2860,7 @@ struct ContextFormat3
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverageZ[0]).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount)); const LookupRecord *lookupRecord = &StructAfter<LookupRecord> (coverageZ.as_array (glyphCount));
struct ContextApplyLookupContext lookup_context = { struct ContextApplyLookupContext lookup_context = {
@ -3650,7 +3674,7 @@ struct ChainContextFormat1_4
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
const ChainRuleSet &rule_set = this+ruleSet[index]; const ChainRuleSet &rule_set = this+ruleSet[index];
struct ChainContextApplyLookupContext lookup_context = { struct ChainContextApplyLookupContext lookup_context = {
@ -3861,28 +3885,37 @@ struct ChainContextFormat2_5
unsigned cache_cost () const unsigned cache_cost () const
{ {
unsigned c = (this+lookaheadClassDef).cost () * ruleSet.len; return (this+lookaheadClassDef).cost () * ruleSet.len;
return c >= 4 ? c : 0;
} }
bool cache_func (hb_ot_apply_context_t *c, bool enter) const static void * cache_func (void *p, hb_ot_lookup_cache_op_t op)
{ {
if (enter) switch (op)
{ {
case hb_ot_lookup_cache_op_t::CREATE:
return (void *) true;
case hb_ot_lookup_cache_op_t::ENTER:
{
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable)) if (!HB_BUFFER_TRY_ALLOCATE_VAR (c->buffer, syllable))
return false; return (void *) false;
auto &info = c->buffer->info; auto &info = c->buffer->info;
unsigned count = c->buffer->len; unsigned count = c->buffer->len;
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
info[i].syllable() = 255; info[i].syllable() = 255;
c->new_syllables = 255; c->new_syllables = 255;
return true; return (void *) true;
} }
else case hb_ot_lookup_cache_op_t::LEAVE:
{ {
hb_ot_apply_context_t *c = (hb_ot_apply_context_t *) p;
c->new_syllables = (unsigned) -1; c->new_syllables = (unsigned) -1;
HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable); HB_BUFFER_DEALLOCATE_VAR (c->buffer, syllable);
return true; return nullptr;
} }
case hb_ot_lookup_cache_op_t::DESTROY:
return nullptr;
}
return nullptr;
} }
bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); } bool apply_cached (hb_ot_apply_context_t *c) const { return _apply (c, true); }
@ -3891,7 +3924,7 @@ struct ChainContextFormat2_5
{ {
TRACE_APPLY (this); TRACE_APPLY (this);
unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+coverage).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
const ClassDef &backtrack_class_def = this+backtrackClassDef; const ClassDef &backtrack_class_def = this+backtrackClassDef;
const ClassDef &input_class_def = this+inputClassDef; const ClassDef &input_class_def = this+inputClassDef;
@ -4137,7 +4170,7 @@ struct ChainContextFormat3
const auto &input = StructAfter<decltype (inputX)> (backtrack); const auto &input = StructAfter<decltype (inputX)> (backtrack);
unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint); unsigned int index = (this+input[0]).get_coverage (c->buffer->cur().codepoint);
if (likely (index == NOT_COVERED)) return_trace (false); if (index == NOT_COVERED) return_trace (false);
const auto &lookahead = StructAfter<decltype (lookaheadX)> (input); const auto &lookahead = StructAfter<decltype (lookaheadX)> (input);
const auto &lookup = StructAfter<decltype (lookupX)> (lookahead); const auto &lookup = StructAfter<decltype (lookupX)> (lookahead);
@ -4408,7 +4441,18 @@ struct hb_ot_layout_lookup_accelerator_t
thiz->digest.union_ (subtable.digest); thiz->digest.union_ (subtable.digest);
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (c_accelerate_subtables.cache_user_cost < 4)
c_accelerate_subtables.cache_user_idx = (unsigned) -1;
thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx; thiz->cache_user_idx = c_accelerate_subtables.cache_user_idx;
if (thiz->cache_user_idx != (unsigned) -1)
{
thiz->cache = thiz->subtables[thiz->cache_user_idx].cache_func (nullptr, hb_ot_lookup_cache_op_t::CREATE);
if (!thiz->cache)
thiz->cache_user_idx = (unsigned) -1;
}
for (unsigned i = 0; i < count; i++) for (unsigned i = 0; i < count; i++)
if (i != thiz->cache_user_idx) if (i != thiz->cache_user_idx)
thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func; thiz->subtables[i].apply_cached_func = thiz->subtables[i].apply_func;
@ -4417,6 +4461,17 @@ struct hb_ot_layout_lookup_accelerator_t
return thiz; return thiz;
} }
void fini ()
{
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (cache)
{
assert (cache_user_idx != (unsigned) -1);
subtables[cache_user_idx].cache_func (cache, hb_ot_lookup_cache_op_t::DESTROY);
}
#endif
}
bool may_have (hb_codepoint_t g) const bool may_have (hb_codepoint_t g) const
{ return digest.may_have (g); } { return digest.may_have (g); }
@ -4425,6 +4480,7 @@ struct hb_ot_layout_lookup_accelerator_t
#endif #endif
bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const bool apply (hb_ot_apply_context_t *c, unsigned subtables_count, bool use_cache) const
{ {
c->lookup_accel = this;
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
if (use_cache) if (use_cache)
{ {
@ -4464,10 +4520,13 @@ struct hb_ot_layout_lookup_accelerator_t
hb_set_digest_t digest; hb_set_digest_t digest;
private:
#ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE #ifndef HB_NO_OT_LAYOUT_LOOKUP_CACHE
public:
void *cache = nullptr;
private:
unsigned cache_user_idx = (unsigned) -1; unsigned cache_user_idx = (unsigned) -1;
#endif #endif
private:
hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY]; hb_accelerate_subtables_context_t::hb_applicable_t subtables[HB_VAR_ARRAY];
}; };
@ -4852,7 +4911,12 @@ struct GSUBGPOS
~accelerator_t () ~accelerator_t ()
{ {
for (unsigned int i = 0; i < this->lookup_count; i++) for (unsigned int i = 0; i < this->lookup_count; i++)
hb_free (this->accels[i]); {
auto *accel = this->accels[i].get_relaxed ();
if (accel)
accel->fini ();
hb_free (accel);
}
hb_free (this->accels); hb_free (this->accels);
this->table.destroy (); this->table.destroy ();
} }
@ -4873,6 +4937,7 @@ struct GSUBGPOS
if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel))) if (unlikely (!accels[lookup_index].cmpexch (nullptr, accel)))
{ {
accel->fini ();
hb_free (accel); hb_free (accel);
goto retry; goto retry;
} }

View File

@ -1923,9 +1923,10 @@ apply_forward (OT::hb_ot_apply_context_t *c,
while (buffer->idx < buffer->len && buffer->successful) while (buffer->idx < buffer->len && buffer->successful)
{ {
bool applied = false; bool applied = false;
if (accel.digest.may_have (buffer->cur().codepoint) && auto &cur = buffer->cur();
(buffer->cur().mask & c->lookup_mask) && if (accel.digest.may_have (cur.codepoint) &&
c->check_glyph_property (&buffer->cur(), c->lookup_props)) (cur.mask & c->lookup_mask) &&
c->check_glyph_property (&cur, c->lookup_props))
{ {
applied = accel.apply (c, subtable_count, use_cache); applied = accel.apply (c, subtable_count, use_cache);
} }
@ -1951,9 +1952,10 @@ apply_backward (OT::hb_ot_apply_context_t *c,
hb_buffer_t *buffer = c->buffer; hb_buffer_t *buffer = c->buffer;
do do
{ {
if (accel.digest.may_have (buffer->cur().codepoint) && auto &cur = buffer->cur();
(buffer->cur().mask & c->lookup_mask) && if (accel.digest.may_have (cur.codepoint) &&
c->check_glyph_property (&buffer->cur(), c->lookup_props)) (cur.mask & c->lookup_mask) &&
c->check_glyph_property (&cur, c->lookup_props))
ret |= accel.apply (c, subtable_count, false); ret |= accel.apply (c, subtable_count, false);
/* The reverse lookup doesn't "advance" cursor (for good reason). */ /* The reverse lookup doesn't "advance" cursor (for good reason). */
@ -2033,7 +2035,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
* (plus some past glyphs). * (plus some past glyphs).
* *
* Only try applying the lookup if there is any overlap. */ * Only try applying the lookup if there is any overlap. */
if (accel->digest.may_have (c.digest)) if (accel->digest.may_intersect (c.digest))
{ {
c.set_lookup_index (lookup_index); c.set_lookup_index (lookup_index);
c.set_lookup_mask (lookup.mask, false); c.set_lookup_mask (lookup.mask, false);
@ -2059,7 +2061,7 @@ inline void hb_ot_map_t::apply (const Proxy &proxy,
if (stage->pause_func (plan, font, buffer)) if (stage->pause_func (plan, font, buffer))
{ {
/* Refresh working buffer digest since buffer changed. */ /* Refresh working buffer digest since buffer changed. */
c.digest = buffer->digest (); buffer->collect_codepoints (c.digest);
} }
} }
} }

View File

@ -339,6 +339,11 @@ _hb_glyph_info_is_default_ignorable (const hb_glyph_info_t *info)
!_hb_glyph_info_substituted (info); !_hb_glyph_info_substituted (info);
} }
static inline void static inline void
_hb_glyph_info_set_default_ignorable (hb_glyph_info_t *info)
{
info->unicode_props() |= UPROPS_MASK_IGNORABLE;
}
static inline void
_hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info) _hb_glyph_info_clear_default_ignorable (hb_glyph_info_t *info)
{ {
info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE; info->unicode_props() &= ~ UPROPS_MASK_IGNORABLE;
@ -360,7 +365,7 @@ _hb_glyph_info_set_continuation (hb_glyph_info_t *info)
info->unicode_props() |= UPROPS_MASK_CONTINUATION; info->unicode_props() |= UPROPS_MASK_CONTINUATION;
} }
static inline void static inline void
_hb_glyph_info_reset_continuation (hb_glyph_info_t *info) _hb_glyph_info_clear_continuation (hb_glyph_info_t *info)
{ {
info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION; info->unicode_props() &= ~ UPROPS_MASK_CONTINUATION;
} }
@ -633,8 +638,7 @@ _hb_buffer_assert_gsubgpos_vars (hb_buffer_t *buffer)
} }
/* Make sure no one directly touches our props... */ /* Make sure no one directly touches our props... */
#undef unicode_props0 #undef unicode_props
#undef unicode_props1
#undef lig_props #undef lig_props
#undef glyph_props #undef glyph_props

View File

@ -390,5 +390,19 @@ hb_ot_map_builder_t::compile (hb_ot_map_t &m,
} }
} }
unsigned int hb_ot_map_t::get_feature_tags (unsigned int start_offset, unsigned int *tag_count, hb_tag_t *tags) const
{
if (tag_count)
{
auto sub_features = features.as_array ().sub_array (start_offset, tag_count);
if (tags)
{
for (unsigned int i = 0; i < sub_features.length; i++)
tags[i] = sub_features[i].tag;
}
}
return features.length;
}
#endif #endif

View File

@ -166,6 +166,9 @@ struct hb_ot_map_t
const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void substitute (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const; HB_INTERNAL void position (const struct hb_ot_shape_plan_t *plan, hb_font_t *font, hb_buffer_t *buffer) const;
HB_INTERNAL unsigned int get_feature_tags (unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */) const;
public: public:
hb_tag_t chosen_script[2]; hb_tag_t chosen_script[2];

View File

@ -46,6 +46,8 @@
#include "hb-set.hh" #include "hb-set.hh"
#include "hb-aat-layout.hh" #include "hb-aat-layout.hh"
#include "hb-ot-stat-table.hh"
static inline bool static inline bool
_hb_codepoint_is_regional_indicator (hb_codepoint_t u) _hb_codepoint_is_regional_indicator (hb_codepoint_t u)
@ -121,10 +123,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
plan.kern_mask = plan.map.get_mask (kern_tag); plan.kern_mask = plan.map.get_mask (kern_tag);
plan.requested_kerning = !!plan.kern_mask; plan.requested_kerning = !!plan.kern_mask;
#endif #endif
#ifndef HB_NO_AAT_SHAPE
plan.trak_mask = plan.map.get_mask (HB_TAG ('t','r','a','k'));
plan.requested_tracking = !!plan.trak_mask;
#endif
bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX; bool has_gpos_kern = plan.map.get_feature_index (1, kern_tag) != HB_OT_LAYOUT_NO_FEATURE_INDEX;
bool disable_gpos = plan.shaper->gpos_tag && bool disable_gpos = plan.shaper->gpos_tag &&
@ -207,9 +205,6 @@ hb_ot_shape_planner_t::compile (hb_ot_shape_plan_t &plan,
https://github.com/harfbuzz/harfbuzz/issues/2967. */ https://github.com/harfbuzz/harfbuzz/issues/2967. */
if (plan.apply_morx) if (plan.apply_morx)
plan.adjust_mark_positioning_when_zeroing = false; plan.adjust_mark_positioning_when_zeroing = false;
/* Currently we always apply trak. */
plan.apply_trak = plan.requested_tracking && hb_aat_layout_has_tracking (face);
#endif #endif
} }
@ -274,11 +269,6 @@ hb_ot_shape_plan_t::position (hb_font_t *font,
#endif #endif
else if (this->apply_fallback_kern) else if (this->apply_fallback_kern)
_hb_ot_shape_fallback_kern (this, font, buffer); _hb_ot_shape_fallback_kern (this, font, buffer);
#ifndef HB_NO_AAT_SHAPE
if (this->apply_trak)
hb_aat_layout_track (this, font, buffer);
#endif
} }
@ -346,13 +336,6 @@ hb_ot_shape_collect_features (hb_ot_shape_planner_t *planner,
/* Random! */ /* Random! */
map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE); map->enable_feature (HB_TAG ('r','a','n','d'), F_RANDOM, HB_OT_MAP_MAX_VALUE);
#ifndef HB_NO_AAT_SHAPE
/* Tracking. We enable dummy feature here just to allow disabling
* AAT 'trak' table using features.
* https://github.com/harfbuzz/harfbuzz/issues/1303 */
map->enable_feature (HB_TAG ('t','r','a','k'), F_HAS_FALLBACK);
#endif
map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */ map->enable_feature (HB_TAG ('H','a','r','f')); /* Considered required. */
map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */ map->enable_feature (HB_TAG ('H','A','R','F')); /* Considered discretionary. */
@ -1277,6 +1260,36 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
} }
/**
* hb_ot_shape_plan_get_feature_tags:
* @shape_plan: A shaping plan
* @start_offset: The index of first feature to retrieve
* @tag_count: (inout): Input = the maximum number of features to return;
* Output = the actual number of features returned (may be zero)
* @tags: (out) (array length=tag_count): The array of enabled feature
*
* Fetches the list of OpenType feature tags enabled for a shaping plan, if possible.
*
* Return value: Total number of feature tagss.
*
* Since: 10.3.0
*/
unsigned int
hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */)
{
#ifndef HB_NO_OT_SHAPE
return shape_plan->ot.map.get_feature_tags (start_offset, tag_count, tags);
#else
if (tag_count)
*tag_count = 0;
return 0;
#endif
}
/* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */ /* TODO Move this to hb-ot-shape-normalize, make it do decompose, and make it public. */
static void static void
add_char (hb_font_t *font, add_char (hb_font_t *font,

View File

@ -48,6 +48,12 @@ hb_ot_shape_plan_collect_lookups (hb_shape_plan_t *shape_plan,
hb_tag_t table_tag, hb_tag_t table_tag,
hb_set_t *lookup_indexes /* OUT */); hb_set_t *lookup_indexes /* OUT */);
HB_EXTERN unsigned int
hb_ot_shape_plan_get_feature_tags (hb_shape_plan_t *shape_plan,
unsigned int start_offset,
unsigned int *tag_count, /* IN/OUT */
hb_tag_t *tags /* OUT */);
HB_END_DECLS HB_END_DECLS
#endif /* HB_OT_SHAPE_H */ #endif /* HB_OT_SHAPE_H */

View File

@ -51,7 +51,8 @@ struct hb_ot_shape_plan_key_t
bool equal (const hb_ot_shape_plan_key_t *other) bool equal (const hb_ot_shape_plan_key_t *other)
{ {
return 0 == hb_memcmp (this, other, sizeof (*this)); return variations_index[0] == other->variations_index[0] &&
variations_index[1] == other->variations_index[1];
} }
}; };
@ -79,22 +80,12 @@ struct hb_ot_shape_plan_t
#else #else
static constexpr hb_mask_t kern_mask = 0; static constexpr hb_mask_t kern_mask = 0;
#endif #endif
#ifndef HB_NO_AAT_SHAPE
hb_mask_t trak_mask;
#else
static constexpr hb_mask_t trak_mask = 0;
#endif
#ifndef HB_NO_OT_KERN #ifndef HB_NO_OT_KERN
bool requested_kerning : 1; bool requested_kerning : 1;
#else #else
static constexpr bool requested_kerning = false; static constexpr bool requested_kerning = false;
#endif #endif
#ifndef HB_NO_AAT_SHAPE
bool requested_tracking : 1;
#else
static constexpr bool requested_tracking = false;
#endif
#ifndef HB_NO_OT_SHAPE_FRACTIONS #ifndef HB_NO_OT_SHAPE_FRACTIONS
bool has_frac : 1; bool has_frac : 1;
#else #else
@ -117,11 +108,9 @@ struct hb_ot_shape_plan_t
#ifndef HB_NO_AAT_SHAPE #ifndef HB_NO_AAT_SHAPE
bool apply_kerx : 1; bool apply_kerx : 1;
bool apply_morx : 1; bool apply_morx : 1;
bool apply_trak : 1;
#else #else
static constexpr bool apply_kerx = false; static constexpr bool apply_kerx = false;
static constexpr bool apply_morx = false; static constexpr bool apply_morx = false;
static constexpr bool apply_trak = false;
#endif #endif
void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const void collect_lookups (hb_tag_t table_tag, hb_set_t *lookups) const

View File

@ -355,6 +355,8 @@ arabic_fallback_plan_destroy (arabic_fallback_plan_t *fallback_plan)
for (unsigned int i = 0; i < fallback_plan->num_lookups; i++) for (unsigned int i = 0; i < fallback_plan->num_lookups; i++)
if (fallback_plan->lookup_array[i]) if (fallback_plan->lookup_array[i])
{ {
if (fallback_plan->accel_array[i])
fallback_plan->accel_array[i]->fini ();
hb_free (fallback_plan->accel_array[i]); hb_free (fallback_plan->accel_array[i]);
if (fallback_plan->free_lookups) if (fallback_plan->free_lookups)
hb_free (fallback_plan->lookup_array[i]); hb_free (fallback_plan->lookup_array[i]);

View File

@ -24,7 +24,7 @@ static void
_output_dotted_circle (hb_buffer_t *buffer) _output_dotted_circle (hb_buffer_t *buffer)
{ {
(void) buffer->output_glyph (0x25CCu); (void) buffer->output_glyph (0x25CCu);
_hb_glyph_info_reset_continuation (&buffer->prev()); _hb_glyph_info_clear_continuation (&buffer->prev());
} }
static void static void

View File

@ -6,8 +6,8 @@
* *
* on files with these headers: * on files with these headers:
* *
* <meta name="updated_at" content="2024-12-05 07:13 PM" /> * <meta name="updated_at" content="2024-12-06 06:35 AM" />
* File-Date: 2024-11-19 * File-Date: 2025-01-21
*/ */
#ifndef HB_OT_TAG_TABLE_HH #ifndef HB_OT_TAG_TABLE_HH
@ -745,6 +745,7 @@ static const LangTag ot_languages3[] = {
/*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */ /*{HB_TAG('h','n','d',' '), HB_TAG('H','N','D',' ')},*/ /* Southern Hindko -> Hindko */
{HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */ {HB_TAG('h','n','e',' '), HB_TAG('C','H','H',' ')}, /* Chhattisgarhi -> Chattisgarhi */
{HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */ {HB_TAG('h','n','j',' '), HB_TAG('H','M','N',' ')}, /* Hmong Njua -> Hmong */
{HB_TAG('h','n','m',' '), HB_TAG('Z','H','S',' ')}, /* Hainanese -> Chinese, Simplified */
{HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */ {HB_TAG('h','n','o',' '), HB_TAG('H','N','D',' ')}, /* Northern Hindko -> Hindko */
{HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */ {HB_TAG('h','o','c',' '), HB_TAG('H','O',' ',' ')}, /* Ho */
{HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */ {HB_TAG('h','o','i',' '), HB_TAG('A','T','H',' ')}, /* Holikachuk -> Athapaskan */
@ -981,9 +982,11 @@ static const LangTag ot_languages3[] = {
{HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */ {HB_TAG('l','t','o',' '), HB_TAG('L','U','H',' ')}, /* Tsotso -> Luyia */
{HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */ {HB_TAG('l','t','s',' '), HB_TAG('L','U','H',' ')}, /* Tachoni -> Luyia */
/*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */ /*{HB_TAG('l','u','a',' '), HB_TAG('L','U','A',' ')},*/ /* Luba-Lulua */
{HB_TAG('l','u','h',' '), HB_TAG('Z','H','S',' ')}, /* Leizhou Chinese -> Chinese, Simplified */
/*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */ /*{HB_TAG('l','u','o',' '), HB_TAG('L','U','O',' ')},*/ /* Luo (Kenya and Tanzania) */
{HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */ {HB_TAG('l','u','s',' '), HB_TAG('M','I','Z',' ')}, /* Lushai -> Mizo */
{HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */ {HB_TAG('l','u','s',' '), HB_TAG('Q','I','N',' ')}, /* Lushai -> Chin */
/*{HB_TAG('l','u','t',' '), HB_TAG('L','U','T',' ')},*/ /* Lushootseed */
{HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */ {HB_TAG('l','u','y',' '), HB_TAG('L','U','H',' ')}, /* Luyia [macrolanguage] */
{HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */ {HB_TAG('l','u','z',' '), HB_TAG('L','R','C',' ')}, /* Southern Luri -> Luri */
{HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */ {HB_TAG('l','v','i',' '), HB_TAG_NONE }, /* Lavi != Latvian */
@ -1404,6 +1407,7 @@ static const LangTag ot_languages3[] = {
{HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */ {HB_TAG('s','i','g',' '), HB_TAG_NONE }, /* Paasaal != Silte Gurage */
{HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */ {HB_TAG('s','i','z',' '), HB_TAG('B','B','R',' ')}, /* Siwi -> Berber */
/*{HB_TAG('s','j','a',' '), HB_TAG('S','J','A',' ')},*/ /* Epena */ /*{HB_TAG('s','j','a',' '), HB_TAG('S','J','A',' ')},*/ /* Epena */
{HB_TAG('s','j','c',' '), HB_TAG('Z','H','S',' ')}, /* Shaojiang Chinese -> Chinese, Simplified */
{HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */ {HB_TAG('s','j','d',' '), HB_TAG('K','S','M',' ')}, /* Kildin Sami */
/*{HB_TAG('s','j','e',' '), HB_TAG('S','J','E',' ')},*/ /* Pite Sami */ /*{HB_TAG('s','j','e',' '), HB_TAG('S','J','E',' ')},*/ /* Pite Sami */
{HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */ {HB_TAG('s','j','o',' '), HB_TAG('S','I','B',' ')}, /* Xibe -> Sibe */
@ -2386,6 +2390,26 @@ out:
*count = i; *count = i;
return true; return true;
} }
if (lang_matches (&lang_str[1], limit, "nm-hant-hk", 10))
{
/* Hainanese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hant-mo", 10))
{
/* Hainanese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10)) if (lang_matches (&lang_str[1], limit, "sn-hant-hk", 10))
{ {
/* Xiang Chinese; Han (Traditional variant); Hong Kong */ /* Xiang Chinese; Han (Traditional variant); Hong Kong */
@ -2420,6 +2444,20 @@ out:
*count = 1; *count = 1;
return true; return true;
} }
if (lang_matches (&lang_str[1], limit, "nm-hans", 7))
{
/* Hainanese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "nm-hant", 7))
{
/* Hainanese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "sn-hans", 7)) if (lang_matches (&lang_str[1], limit, "sn-hans", 7))
{ {
/* Xiang Chinese; Han (Simplified variant) */ /* Xiang Chinese; Han (Simplified variant) */
@ -2464,6 +2502,36 @@ out:
*count = 1; *count = 1;
return true; return true;
} }
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Hainanese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Hainanese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "nm-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Hainanese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "sn-", 3) if (0 == strncmp (&lang_str[1], "sn-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3)) && subtag_matches (lang_str, limit, "-hk", 3))
{ {
@ -2525,6 +2593,40 @@ out:
} }
break; break;
case 'l': case 'l':
if (lang_matches (&lang_str[1], limit, "uh-hant-hk", 10))
{
/* Leizhou Chinese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hant-mo", 10))
{
/* Leizhou Chinese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hans", 7))
{
/* Leizhou Chinese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "uh-hant", 7))
{
/* Leizhou Chinese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "zh-hans", 7)) if (lang_matches (&lang_str[1], limit, "zh-hans", 7))
{ {
/* Literary Chinese; Han (Simplified variant) */ /* Literary Chinese; Han (Simplified variant) */
@ -2532,6 +2634,36 @@ out:
*count = 1; *count = 1;
return true; return true;
} }
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Leizhou Chinese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Leizhou Chinese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "uh-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Leizhou Chinese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
break; break;
case 'm': case 'm':
if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10)) if (lang_matches (&lang_str[1], limit, "np-hant-hk", 10))
@ -2703,6 +2835,72 @@ out:
return true; return true;
} }
break; break;
case 's':
if (lang_matches (&lang_str[1], limit, "jc-hant-hk", 10))
{
/* Shaojiang Chinese; Han (Traditional variant); Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hant-mo", 10))
{
/* Shaojiang Chinese; Han (Traditional variant); Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hans", 7))
{
/* Shaojiang Chinese; Han (Simplified variant) */
tags[0] = HB_TAG('Z','H','S',' '); /* Chinese, Simplified */
*count = 1;
return true;
}
if (lang_matches (&lang_str[1], limit, "jc-hant", 7))
{
/* Shaojiang Chinese; Han (Traditional variant) */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-hk", 3))
{
/* Shaojiang Chinese; Hong Kong */
tags[0] = HB_TAG('Z','H','H',' '); /* Chinese, Traditional, Hong Kong SAR */
*count = 1;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-mo", 3))
{
/* Shaojiang Chinese; Macao */
unsigned int i;
hb_tag_t possible_tags[] = {
HB_TAG('Z','H','T','M'), /* Chinese, Traditional, Macao SAR */
HB_TAG('Z','H','H',' '), /* Chinese, Traditional, Hong Kong SAR */
};
for (i = 0; i < 2 && i < *count; i++)
tags[i] = possible_tags[i];
*count = i;
return true;
}
if (0 == strncmp (&lang_str[1], "jc-", 3)
&& subtag_matches (lang_str, limit, "-tw", 3))
{
/* Shaojiang Chinese; Taiwan, Province of China */
tags[0] = HB_TAG('Z','H','T',' '); /* Chinese, Traditional */
*count = 1;
return true;
}
break;
case 'w': case 'w':
if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10)) if (lang_matches (&lang_str[1], limit, "uu-hant-hk", 10))
{ {

View File

@ -72,7 +72,7 @@ struct glyph_variations_t
const hb_subset_plan_t *plan, const hb_subset_plan_t *plan,
const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map) const hb_hashmap_t<hb_codepoint_t, hb_bytes_t>& new_gid_var_data_map)
{ {
if (unlikely (!glyph_variations.alloc (plan->new_to_old_gid_list.length, true))) if (unlikely (!glyph_variations.alloc_exact (plan->new_to_old_gid_list.length)))
return false; return false;
auto it = hb_iter (plan->new_to_old_gid_list); auto it = hb_iter (plan->new_to_old_gid_list);

View File

@ -78,11 +78,11 @@ struct hb_serialize_context_t
head = o.head; head = o.head;
tail = o.tail; tail = o.tail;
next = nullptr; next = nullptr;
real_links.alloc (o.num_real_links, true); real_links.alloc_exact (o.num_real_links);
for (unsigned i = 0 ; i < o.num_real_links; i++) for (unsigned i = 0 ; i < o.num_real_links; i++)
real_links.push (o.real_links[i]); real_links.push (o.real_links[i]);
virtual_links.alloc (o.num_virtual_links, true); virtual_links.alloc_exact (o.num_virtual_links);
for (unsigned i = 0; i < o.num_virtual_links; i++) for (unsigned i = 0; i < o.num_virtual_links; i++)
virtual_links.push (o.virtual_links[i]); virtual_links.push (o.virtual_links[i]);
} }

View File

@ -56,7 +56,7 @@
* - For each glyph, if it doesn't match the subtable digest, * - For each glyph, if it doesn't match the subtable digest,
* skip it. * skip it.
* *
* The main filter we use is a combination of three bits-pattern * The main filter we use is a combination of four bits-pattern
* filters. A bits-pattern filter checks a number of bits (5 or 6) * filters. A bits-pattern filter checks a number of bits (5 or 6)
* of the input number (glyph-id in this case) and checks whether * of the input number (glyph-id in this case) and checks whether
* its pattern is amongst the patterns of any of the accepted values. * its pattern is amongst the patterns of any of the accepted values.
@ -64,46 +64,61 @@
* check is done using four bitwise operations only. * check is done using four bitwise operations only.
*/ */
template <typename mask_t, unsigned int shift> static constexpr unsigned hb_set_digest_shifts[] = {4, 0, 6};
struct hb_set_digest_bits_pattern_t
struct hb_set_digest_t
{ {
// No science in these. Intuition and testing only.
using mask_t = uint64_t;
static constexpr unsigned n = ARRAY_LENGTH_CONST (hb_set_digest_shifts);
static constexpr unsigned mask_bytes = sizeof (mask_t); static constexpr unsigned mask_bytes = sizeof (mask_t);
static constexpr unsigned mask_bits = sizeof (mask_t) * 8; static constexpr unsigned mask_bits = sizeof (mask_t) * 8;
static constexpr unsigned num_bits = 0 static constexpr hb_codepoint_t mb1 = mask_bits - 1;
+ (mask_bytes >= 1 ? 3 : 0) static constexpr mask_t one = 1;
+ (mask_bytes >= 2 ? 1 : 0) static constexpr mask_t all = (mask_t) -1;
+ (mask_bytes >= 4 ? 1 : 0)
+ (mask_bytes >= 8 ? 1 : 0)
+ (mask_bytes >= 16? 1 : 0)
+ 0;
static_assert ((shift < sizeof (hb_codepoint_t) * 8), ""); void init ()
static_assert ((shift + num_bits <= sizeof (hb_codepoint_t) * 8), ""); { for (unsigned i = 0; i < n; i++) masks[i] = 0; }
void init () { mask = 0; } void clear () { init (); }
static hb_set_digest_bits_pattern_t full () { hb_set_digest_bits_pattern_t d; d.mask = (mask_t) -1; return d; } static hb_set_digest_t full ()
{
hb_set_digest_t d;
for (unsigned i = 0; i < n; i++) d.masks[i] = all;
return d;
}
void union_ (const hb_set_digest_bits_pattern_t &o) { mask |= o.mask; } void union_ (const hb_set_digest_t &o)
{ for (unsigned i = 0; i < n; i++) masks[i] |= o.masks[i]; }
void add (hb_codepoint_t g) { mask |= mask_for (g); }
bool add_range (hb_codepoint_t a, hb_codepoint_t b) bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{ {
if (mask == (mask_t) -1) return false; bool ret;
if ((b >> shift) - (a >> shift) >= mask_bits - 1)
ret = false;
for (unsigned i = 0; i < n; i++)
if (masks[i] != all)
ret = true;
if (!ret) return false;
ret = false;
for (unsigned i = 0; i < n; i++)
{ {
mask = (mask_t) -1; mask_t shift = hb_set_digest_shifts[i];
return false; if ((b >> shift) - (a >> shift) >= mb1)
} masks[i] = all;
else else
{ {
mask_t ma = mask_for (a); mask_t ma = one << ((a >> shift) & mb1);
mask_t mb = mask_for (b); mask_t mb = one << ((b >> shift) & mb1);
mask |= mb + (mb - ma) - (mb < ma); masks[i] |= mb + (mb - ma) - (mb < ma);
return true; ret = true;
} }
} }
return ret;
}
template <typename T> template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T)) void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
@ -125,103 +140,37 @@ struct hb_set_digest_bits_pattern_t
template <typename T> template <typename T>
bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); } bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
bool may_have (const hb_set_digest_bits_pattern_t &o) const
{ return mask & o.mask; }
bool may_have (hb_codepoint_t g) const
{ return mask & mask_for (g); }
bool operator [] (hb_codepoint_t g) const bool operator [] (hb_codepoint_t g) const
{ return may_have (g); } { return may_have (g); }
private:
static mask_t mask_for (hb_codepoint_t g)
{ return ((mask_t) 1) << ((g >> shift) & (mask_bits - 1)); }
mask_t mask = 0;
};
template <typename head_t, typename tail_t>
struct hb_set_digest_combiner_t
{
void init ()
{
head.init ();
tail.init ();
}
static hb_set_digest_combiner_t full () { hb_set_digest_combiner_t d; d.head = head_t::full(); d.tail = tail_t::full (); return d; }
void union_ (const hb_set_digest_combiner_t &o)
{
head.union_ (o.head);
tail.union_(o.tail);
}
void add (hb_codepoint_t g) void add (hb_codepoint_t g)
{ {
head.add (g); for (unsigned i = 0; i < n; i++)
tail.add (g); masks[i] |= one << ((g >> hb_set_digest_shifts[i]) & mb1);
}
bool add_range (hb_codepoint_t a, hb_codepoint_t b)
{
return (int) head.add_range (a, b) | (int) tail.add_range (a, b);
}
template <typename T>
void add_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{
head.add_array (array, count, stride);
tail.add_array (array, count, stride);
}
template <typename T>
void add_array (const hb_array_t<const T>& arr) { add_array (&arr, arr.len ()); }
template <typename T>
bool add_sorted_array (const T *array, unsigned int count, unsigned int stride=sizeof(T))
{
return head.add_sorted_array (array, count, stride) &&
tail.add_sorted_array (array, count, stride);
}
template <typename T>
bool add_sorted_array (const hb_sorted_array_t<const T>& arr) { return add_sorted_array (&arr, arr.len ()); }
bool may_have (const hb_set_digest_combiner_t &o) const
{
return head.may_have (o.head) && tail.may_have (o.tail);
} }
HB_ALWAYS_INLINE
bool may_have (hb_codepoint_t g) const bool may_have (hb_codepoint_t g) const
{ {
return head.may_have (g) && tail.may_have (g); for (unsigned i = 0; i < n; i++)
if (!(masks[i] & (one << ((g >> hb_set_digest_shifts[i]) & mb1))))
return false;
return true;
} }
bool operator [] (hb_codepoint_t g) const bool may_intersect (const hb_set_digest_t &o) const
{ return may_have (g); } {
for (unsigned i = 0; i < n; i++)
if (!(masks[i] & o.masks[i]))
return false;
return true;
}
private: private:
head_t head;
tail_t tail; mask_t masks[n] = {};
}; };
/*
* hb_set_digest_t
*
* This is a combination of digests that performs "best".
* There is not much science to this: it's a result of intuition
* and testing.
*/
using hb_set_digest_t =
hb_set_digest_combiner_t
<
hb_set_digest_bits_pattern_t<unsigned long, 4>,
hb_set_digest_combiner_t
<
hb_set_digest_bits_pattern_t<unsigned long, 0>,
hb_set_digest_bits_pattern_t<unsigned long, 9>
>
>
;
#endif /* HB_SET_DIGEST_HH */ #endif /* HB_SET_DIGEST_HH */

View File

@ -106,6 +106,7 @@ struct hb_sparseset_t
void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); } void del_range (hb_codepoint_t a, hb_codepoint_t b) { s.del_range (a, b); }
bool get (hb_codepoint_t g) const { return s.get (g); } bool get (hb_codepoint_t g) const { return s.get (g); }
bool may_have (hb_codepoint_t g) const { return get (g); }
/* Has interface. */ /* Has interface. */
bool operator [] (hb_codepoint_t k) const { return get (k); } bool operator [] (hb_codepoint_t k) const { return get (k); }
@ -120,6 +121,9 @@ struct hb_sparseset_t
hb_sparseset_t& operator << (const hb_codepoint_pair_t& range) hb_sparseset_t& operator << (const hb_codepoint_pair_t& range)
{ add_range (range.first, range.second); return *this; } { add_range (range.first, range.second); return *this; }
bool may_intersect (const hb_sparseset_t &other) const
{ return s.may_intersect (other.s); }
bool intersects (hb_codepoint_t first, hb_codepoint_t last) const bool intersects (hb_codepoint_t first, hb_codepoint_t last) const
{ return s.intersects (first, last); } { return s.intersects (first, last); }

View File

@ -233,7 +233,7 @@ hb_shape_plan_create2 (hb_face_t *face,
num_coords, num_coords,
shaper_list); shaper_list);
if (unlikely (props->direction == HB_DIRECTION_INVALID)) if (unlikely (!HB_DIRECTION_IS_VALID (props->direction)))
return hb_shape_plan_get_empty (); return hb_shape_plan_get_empty ();
hb_shape_plan_t *shape_plan; hb_shape_plan_t *shape_plan;

View File

@ -1128,7 +1128,7 @@ struct subr_subsetter_t
if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr) if (opstr.op == OpCode_callsubr || opstr.op == OpCode_callgsubr)
size += 3; size += 3;
} }
if (!buff.alloc (buff.length + size, true)) if (!buff.alloc_exact (buff.length + size))
return false; return false;
for (auto &opstr : str.values) for (auto &opstr : str.values)

View File

@ -45,7 +45,7 @@ struct remap_sid_t
void alloc (unsigned size) void alloc (unsigned size)
{ {
map.alloc (size); map.alloc (size);
vector.alloc (size, true); vector.alloc_exact (size);
} }
bool in_error () const bool in_error () const

View File

@ -296,7 +296,7 @@ _try_subset (const TableType *table,
HB_UNTAG (c->table_tag), buf_size); HB_UNTAG (c->table_tag), buf_size);
if (unlikely (buf_size > c->source_blob->length * 256 || if (unlikely (buf_size > c->source_blob->length * 256 ||
!buf->alloc (buf_size, true))) !buf->alloc_exact (buf_size)))
{ {
DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.", DEBUG_MSG (SUBSET, nullptr, "OT::%c%c%c%c failed to reallocate %u bytes.",
HB_UNTAG (c->table_tag), buf_size); HB_UNTAG (c->table_tag), buf_size);

View File

@ -62,19 +62,19 @@ struct hb_vector_t
} }
hb_vector_t (const hb_vector_t &o) : hb_vector_t () hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
{ {
alloc (o.length, true); alloc_exact (o.length);
if (unlikely (in_error ())) return; if (unlikely (in_error ())) return;
copy_array (o.as_array ()); copy_array (o.as_array ());
} }
hb_vector_t (array_t o) : hb_vector_t () hb_vector_t (array_t o) : hb_vector_t ()
{ {
alloc (o.length, true); alloc_exact (o.length);
if (unlikely (in_error ())) return; if (unlikely (in_error ())) return;
copy_array (o); copy_array (o);
} }
hb_vector_t (c_array_t o) : hb_vector_t () hb_vector_t (c_array_t o) : hb_vector_t ()
{ {
alloc (o.length, true); alloc_exact (o.length);
if (unlikely (in_error ())) return; if (unlikely (in_error ())) return;
copy_array (o); copy_array (o);
} }
@ -132,7 +132,7 @@ struct hb_vector_t
hb_vector_t& operator = (const hb_vector_t &o) hb_vector_t& operator = (const hb_vector_t &o)
{ {
reset (); reset ();
alloc (o.length, true); alloc_exact (o.length);
if (unlikely (in_error ())) return *this; if (unlikely (in_error ())) return *this;
copy_array (o.as_array ()); copy_array (o.as_array ());
@ -432,6 +432,10 @@ struct hb_vector_t
return true; return true;
} }
bool alloc_exact (unsigned int size)
{
return alloc (size, true);
}
bool resize (int size_, bool initialize = true, bool exact = false) bool resize (int size_, bool initialize = true, bool exact = false)
{ {
@ -497,7 +501,7 @@ struct hb_vector_t
shrink_vector (size); shrink_vector (size);
if (shrink_memory) if (shrink_memory)
alloc (size, true); /* To force shrinking memory if needed. */ alloc_exact (size); /* To force shrinking memory if needed. */
} }

View File

@ -47,7 +47,7 @@ HB_BEGIN_DECLS
* *
* The minor component of the library version available at compile-time. * The minor component of the library version available at compile-time.
*/ */
#define HB_VERSION_MINOR 2 #define HB_VERSION_MINOR 3
/** /**
* HB_VERSION_MICRO: * HB_VERSION_MICRO:
* *
@ -60,7 +60,7 @@ HB_BEGIN_DECLS
* *
* A string literal containing the library version available at compile-time. * A string literal containing the library version available at compile-time.
*/ */
#define HB_VERSION_STRING "10.2.0" #define HB_VERSION_STRING "10.3.0"
/** /**
* HB_VERSION_ATLEAST: * HB_VERSION_ATLEAST:

View File

@ -131,6 +131,7 @@
#pragma GCC diagnostic ignored "-Wclass-memaccess" #pragma GCC diagnostic ignored "-Wclass-memaccess"
#pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126 #pragma GCC diagnostic ignored "-Wcast-function-type-strict" // https://github.com/harfbuzz/harfbuzz/pull/3859#issuecomment-1295409126
#pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043 #pragma GCC diagnostic ignored "-Wdangling-reference" // https://github.com/harfbuzz/harfbuzz/issues/4043
#pragma GCC diagnostic ignored "-Wdangling-pointer" // Trigerred by hb_decycler_node_t().
#pragma GCC diagnostic ignored "-Wformat-nonliteral" #pragma GCC diagnostic ignored "-Wformat-nonliteral"
#pragma GCC diagnostic ignored "-Wformat-zero-length" #pragma GCC diagnostic ignored "-Wformat-zero-length"
#pragma GCC diagnostic ignored "-Wmissing-field-initializers" #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
@ -281,7 +282,9 @@ extern "C" void hb_free_impl(void *ptr);
#define __attribute__(x) #define __attribute__(x)
#endif #endif
#if defined(__GNUC__) && (__GNUC__ >= 3) #if defined(__MINGW32__) && (__GNUC__ >= 3)
#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (gnu_printf, format_idx, arg_idx)))
#elif defined(__GNUC__) && (__GNUC__ >= 3)
#define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx))) #define HB_PRINTF_FUNC(format_idx, arg_idx) __attribute__((__format__ (__printf__, format_idx, arg_idx)))
#else #else
#define HB_PRINTF_FUNC(format_idx, arg_idx) #define HB_PRINTF_FUNC(format_idx, arg_idx)