summaryrefslogtreecommitdiffstats
path: root/src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh
diff options
context:
space:
mode:
Diffstat (limited to 'src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh')
-rw-r--r--src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh154
1 files changed, 90 insertions, 64 deletions
diff --git a/src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh b/src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh
index 722e8599a96..1653174a32b 100644
--- a/src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh
+++ b/src/3rdparty/harfbuzz-ng/src/hb-ot-var-common.hh
@@ -33,6 +33,8 @@
namespace OT {
+using rebase_tent_result_scratch_t = hb_pair_t<rebase_tent_result_t, rebase_tent_result_t>;
+
/* https://docs.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#tuplevariationheader */
struct TupleVariationHeader
{
@@ -320,28 +322,31 @@ struct tuple_delta_t
return *this;
}
- hb_vector_t<tuple_delta_t> change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit,
- TripleDistances axis_triple_distances) const
+ void change_tuple_var_axis_limit (hb_tag_t axis_tag, Triple axis_limit,
+ TripleDistances axis_triple_distances,
+ hb_vector_t<tuple_delta_t>& out,
+ rebase_tent_result_scratch_t &scratch) const
{
- hb_vector_t<tuple_delta_t> out;
+ out.reset ();
Triple *tent;
if (!axis_tuples.has (axis_tag, &tent))
{
out.push (*this);
- return out;
+ return;
}
if ((tent->minimum < 0.0 && tent->maximum > 0.0) ||
!(tent->minimum <= tent->middle && tent->middle <= tent->maximum))
- return out;
+ return;
if (tent->middle == 0.0)
{
out.push (*this);
- return out;
+ return;
}
- rebase_tent_result_t solutions = rebase_tent (*tent, axis_limit, axis_triple_distances);
+ rebase_tent_result_t &solutions = scratch.first;
+ rebase_tent (*tent, axis_limit, axis_triple_distances, solutions, scratch.second);
for (auto &t : solutions)
{
tuple_delta_t new_var = *this;
@@ -353,8 +358,6 @@ struct tuple_delta_t
new_var *= t.first;
out.push (std::move (new_var));
}
-
- return out;
}
bool compile_peak_coords (const hb_map_t& axes_index_map,
@@ -402,7 +405,7 @@ struct tuple_delta_t
unsigned cur_axis_count = axes_index_map.get_population ();
/* allocate enough memory: 1 peak + 2 intermediate coords + fixed header size */
unsigned alloc_len = 3 * cur_axis_count * (F2DOT14::static_size) + 4;
- if (unlikely (!compiled_tuple_header.resize (alloc_len))) return false;
+ if (unlikely (!compiled_tuple_header.resize (alloc_len, false))) return false;
unsigned flag = 0;
/* skip the first 4 header bytes: variationDataSize+tupleIndex */
@@ -535,7 +538,7 @@ struct tuple_delta_t
if (y_deltas)
alloc_len *= 2;
- if (unlikely (!compiled_deltas.resize (alloc_len))) return false;
+ if (unlikely (!compiled_deltas.resize (alloc_len, false))) return false;
unsigned encoded_len = compile_deltas (compiled_deltas, rounded_deltas);
@@ -565,14 +568,17 @@ struct tuple_delta_t
return TupleValues::compile (deltas, encoded_bytes);
}
- bool calc_inferred_deltas (const contour_point_vector_t& orig_points)
+ bool calc_inferred_deltas (const contour_point_vector_t& orig_points,
+ hb_vector_t<unsigned> &scratch)
{
unsigned point_count = orig_points.length;
if (point_count != indices.length)
return false;
unsigned ref_count = 0;
- hb_vector_t<unsigned> end_points;
+
+ hb_vector_t<unsigned> &end_points = scratch;
+ end_points.reset ();
for (unsigned i = 0; i < point_count; i++)
{
@@ -586,7 +592,7 @@ struct tuple_delta_t
return true;
if (unlikely (end_points.in_error ())) return false;
- hb_set_t inferred_idxes;
+ hb_bit_set_t inferred_idxes;
unsigned start_point = 0;
for (unsigned end_point : end_points)
{
@@ -926,6 +932,9 @@ struct TupleVariationData
const hb_array_t<const F2DOT14> shared_tuples,
bool is_composite_glyph)
{
+ hb_vector_t<unsigned> private_indices;
+ hb_vector_t<int> deltas_x;
+ hb_vector_t<int> deltas_y;
do
{
const HBUINT8 *p = iterator.get_serialized_data ();
@@ -933,12 +942,12 @@ struct TupleVariationData
if (unlikely (!iterator.var_data_bytes.check_range (p, length)))
return false;
- hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
+ hb_hashmap_t<hb_tag_t, Triple> axis_tuples;
if (!iterator.current_tuple->unpack_axis_tuples (iterator.get_axis_count (), shared_tuples, axes_old_index_tag_map, axis_tuples)
|| axis_tuples.is_empty ())
return false;
- hb_vector_t<unsigned> private_indices;
+ private_indices.reset ();
bool has_private_points = iterator.current_tuple->has_private_points ();
const HBUINT8 *end = p + length;
if (has_private_points &&
@@ -949,13 +958,10 @@ struct TupleVariationData
bool apply_to_all = (indices.length == 0);
unsigned num_deltas = apply_to_all ? point_count : indices.length;
- hb_vector_t<int> deltas_x;
-
if (unlikely (!deltas_x.resize (num_deltas, false) ||
!TupleVariationData::decompile_deltas (p, deltas_x, end)))
return false;
- hb_vector_t<int> deltas_y;
if (is_gvar)
{
if (unlikely (!deltas_y.resize (num_deltas, false) ||
@@ -1049,6 +1055,10 @@ struct TupleVariationData
for (auto t : normalized_axes_location.keys ())
axis_tags.push (t);
+ // Reused vectors for reduced malloc pressure.
+ rebase_tent_result_scratch_t scratch;
+ hb_vector_t<tuple_delta_t> out;
+
axis_tags.qsort (_cmp_axis_tag);
for (auto axis_tag : axis_tags)
{
@@ -1062,7 +1072,7 @@ struct TupleVariationData
hb_vector_t<tuple_delta_t> new_vars;
for (const tuple_delta_t& var : tuple_vars)
{
- hb_vector_t<tuple_delta_t> out = var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances);
+ var.change_tuple_var_axis_limit (axis_tag, *axis_limit, axis_triple_distances, out, scratch);
if (!out) continue;
unsigned new_len = new_vars.length + out.length;
@@ -1084,9 +1094,12 @@ struct TupleVariationData
bool merge_tuple_variations (contour_point_vector_t* contour_points = nullptr)
{
hb_vector_t<tuple_delta_t> new_vars;
+ // The pre-allocation is essential for address stability of pointers
+ // we store in the hashmap.
+ if (unlikely (!new_vars.alloc (tuple_vars.length)))
+ return false;
hb_hashmap_t<const hb_hashmap_t<hb_tag_t, Triple>*, unsigned> m;
- unsigned i = 0;
- for (const tuple_delta_t& var : tuple_vars)
+ for (tuple_delta_t& var : tuple_vars)
{
/* if all axes are pinned, drop the tuple variation */
if (var.axis_tuples.is_empty ())
@@ -1106,13 +1119,17 @@ struct TupleVariationData
}
else
{
- new_vars.push (var);
- if (!m.set (&(var.axis_tuples), i))
+ auto *new_var = new_vars.push ();
+ if (unlikely (new_vars.in_error ()))
+ return false;
+ hb_swap (*new_var, var);
+ if (unlikely (!m.set (&(new_var->axis_tuples), new_vars.length - 1)))
return false;
- i++;
}
}
- tuple_vars.fini ();
+ m.fini (); // Just in case, since it points into new_vars data.
+ // Shouldn't be necessary though, since we only move new_vars, not its
+ // contents.
tuple_vars = std::move (new_vars);
return true;
}
@@ -1172,10 +1189,11 @@ struct TupleVariationData
}
}
- bool calc_inferred_deltas (const contour_point_vector_t& contour_points)
+ bool calc_inferred_deltas (const contour_point_vector_t& contour_points,
+ hb_vector_t<unsigned> &scratch)
{
for (tuple_delta_t& var : tuple_vars)
- if (!var.calc_inferred_deltas (contour_points))
+ if (!var.calc_inferred_deltas (contour_points, scratch))
return false;
return true;
@@ -1202,8 +1220,11 @@ struct TupleVariationData
return false;
/* compute inferred deltas only for gvar */
if (contour_points)
- if (!calc_inferred_deltas (*contour_points))
- return false;
+ {
+ hb_vector_t<unsigned> scratch;
+ if (!calc_inferred_deltas (*contour_points, scratch))
+ return false;
+ }
/* if iup delta opt is on, contour_points can't be null */
if (optimize && !contour_points)
@@ -1731,30 +1752,28 @@ struct item_variations_t
struct combined_gain_idx_tuple_t
{
- int gain;
- unsigned idx_1;
- unsigned idx_2;
+ uint64_t encoded;
combined_gain_idx_tuple_t () = default;
- combined_gain_idx_tuple_t (int gain_, unsigned i, unsigned j)
- :gain (gain_), idx_1 (i), idx_2 (j) {}
+ combined_gain_idx_tuple_t (unsigned gain, unsigned i, unsigned j)
+ : encoded ((uint64_t (0xFFFFFF - gain) << 40) | (uint64_t (i) << 20) | uint64_t (j))
+ {
+ assert (gain < 0xFFFFFF);
+ assert (i < 0xFFFFFFF && j < 0xFFFFFFF);
+ }
bool operator < (const combined_gain_idx_tuple_t& o)
{
- if (gain != o.gain)
- return gain < o.gain;
-
- if (idx_1 != o.idx_1)
- return idx_1 < o.idx_1;
-
- return idx_2 < o.idx_2;
+ return encoded < o.encoded;
}
bool operator <= (const combined_gain_idx_tuple_t& o)
{
- if (*this < o) return true;
- return gain == o.gain && idx_1 == o.idx_1 && idx_2 == o.idx_2;
+ return encoded <= o.encoded;
}
+
+ unsigned idx_1 () const { return (encoded >> 20) & 0xFFFFF; };
+ unsigned idx_2 () const { return encoded & 0xFFFFF; };
};
bool as_item_varstore (bool optimize=true, bool use_no_variation_idx=true)
@@ -1839,7 +1858,7 @@ struct item_variations_t
if (!front_mapping.set ((major<<16) + minor, &row))
return false;
- hb_vector_t<uint8_t> chars = delta_row_encoding_t::get_row_chars (row);
+ auto chars = delta_row_encoding_t::get_row_chars (row);
if (!chars) return false;
if (delta_rows_map.has (&row))
@@ -1874,7 +1893,8 @@ struct item_variations_t
/* main algorithm: repeatedly pick 2 best encodings to combine, and combine
* them */
- hb_priority_queue_t<combined_gain_idx_tuple_t> queue;
+ using item_t = hb_priority_queue_t<combined_gain_idx_tuple_t>::item_t;
+ hb_vector_t<item_t> queue_items;
unsigned num_todos = encoding_objs.length;
for (unsigned i = 0; i < num_todos; i++)
{
@@ -1882,16 +1902,25 @@ struct item_variations_t
{
int combining_gain = encoding_objs.arrayZ[i].gain_from_merging (encoding_objs.arrayZ[j]);
if (combining_gain > 0)
- queue.insert (combined_gain_idx_tuple_t (-combining_gain, i, j), 0);
+ {
+ auto item = item_t (combined_gain_idx_tuple_t (combining_gain, i, j), 0);
+ queue_items.push (item);
+ }
+
+ // Some heuristic to reduce work we do at the expense of less optimal result.
+ if (num_todos - j > 8 && combining_gain > (int) encoding_objs[j].get_gain ())
+ break;
}
}
- hb_set_t removed_todo_idxes;
+ hb_priority_queue_t<combined_gain_idx_tuple_t> queue (std::move (queue_items));
+
+ hb_bit_set_t removed_todo_idxes;
while (queue)
{
auto t = queue.pop_minimum ().first;
- unsigned i = t.idx_1;
- unsigned j = t.idx_2;
+ unsigned i = t.idx_1 ();
+ unsigned j = t.idx_2 ();
if (removed_todo_idxes.has (i) || removed_todo_idxes.has (j))
continue;
@@ -1902,17 +1931,7 @@ struct item_variations_t
removed_todo_idxes.add (i);
removed_todo_idxes.add (j);
- hb_vector_t<uint8_t> combined_chars;
- if (!combined_chars.alloc (encoding.chars.length))
- return false;
-
- for (unsigned idx = 0; idx < encoding.chars.length; idx++)
- {
- uint8_t v = hb_max (encoding.chars.arrayZ[idx], other_encoding.chars.arrayZ[idx]);
- combined_chars.push (v);
- }
-
- delta_row_encoding_t combined_encoding_obj (std::move (combined_chars));
+ delta_row_encoding_t combined_encoding_obj (std::move (encoding.combine_chars (other_encoding)));
for (const auto& row : hb_concat (encoding.items, other_encoding.items))
combined_encoding_obj.add_row (row);
@@ -1921,8 +1940,15 @@ struct item_variations_t
if (removed_todo_idxes.has (idx)) continue;
const delta_row_encoding_t& obj = encoding_objs.arrayZ[idx];
- if (obj.chars == combined_chars)
+ // In the unlikely event that the same encoding exists already, combine it.
+ if (obj.width == combined_encoding_obj.width && obj.chars == combined_encoding_obj.chars)
{
+ // This is straight port from fonttools algorithm. I added this branch there
+ // because I thought it can happen. But looks like we never get in here in
+ // practice. I'm not confident enough to remove it though; in theory it can
+ // happen. I think it's just that our tests are not extensive enough to hit
+ // this path.
+
for (const auto& row : obj.items)
combined_encoding_obj.add_row (row);
@@ -1932,7 +1958,7 @@ struct item_variations_t
int combined_gain = combined_encoding_obj.gain_from_merging (obj);
if (combined_gain > 0)
- queue.insert (combined_gain_idx_tuple_t (-combined_gain, idx, encoding_objs.length), 0);
+ queue.insert (combined_gain_idx_tuple_t (combined_gain, idx, encoding_objs.length), 0);
}
encoding_objs.push (std::move (combined_encoding_obj));
@@ -1949,7 +1975,7 @@ struct item_variations_t
}
/* sort again based on width, make result deterministic */
- encodings.qsort (delta_row_encoding_t::cmp_width);
+ encodings.qsort ();
return compile_varidx_map (front_mapping);
}