[cff2] Change extents/shape stack to be just a number Do the blending immediately. Fixes https://github.com/harfbuzz/harfbuzz/issues/3559 Benchmark on AdobeVFPrototype shows 35% speedup. Now we're faster than FreeType: Benchmark Time CPU Time Old Time New CPU Old CPU New ------------------------------------------------------------------------------------------------------------------------------------------------ BM_Font/glyph_extents/AdobeVFPrototype.otf/hb -0.3792 -0.3792 1584 983 1581 982 BM_Font/glyph_extents/AdobeVFPrototype.otf/ft +0.0228 +0.0224 1220 1248 1218 1245 BM_Font/glyph_extents/AdobeVFPrototype.otf/var/hb -0.3513 -0.3518 1616 1048 1613 1046 BM_Font/glyph_extents/AdobeVFPrototype.otf/var/ft +0.0172 +0.0169 1232 1254 1230 1251
diff --git a/src/hb-cff-interp-common.hh b/src/hb-cff-interp-common.hh index 2a9e60f..1f924d8 100644 --- a/src/hb-cff-interp-common.hh +++ b/src/hb-cff-interp-common.hh
@@ -430,8 +430,8 @@ unsigned int get_count () const { return count; } bool is_empty () const { return !count; } - hb_array_t<const ELEM> get_subarray (unsigned int start) const - { return hb_array_t<const ELEM> (elements).sub_array (start); } + hb_array_t<const ELEM> sub_array (unsigned start, unsigned length) const + { return hb_array_t<const ELEM> (elements).sub_array (start, length); } private: bool error;
diff --git a/src/hb-cff2-interp-cs.hh b/src/hb-cff2-interp-cs.hh index 60a29e1..6a0b6de 100644 --- a/src/hb-cff2-interp-cs.hh +++ b/src/hb-cff2-interp-cs.hh
@@ -64,7 +64,8 @@ typedef interp_env_t<blend_arg_t> BlendInterpEnv; typedef biased_subrs_t<CFF2Subrs> cff2_biased_subrs_t; -struct cff2_cs_interp_env_t : cs_interp_env_t<blend_arg_t, CFF2Subrs> +template <typename ELEM> +struct cff2_cs_interp_env_t : cs_interp_env_t<ELEM, CFF2Subrs> { template <typename ACC> void init (const hb_ubytes_t &str, ACC &acc, unsigned int fd, @@ -100,18 +101,14 @@ return OpCode_return; } - const blend_arg_t& eval_arg (unsigned int i) + const ELEM& eval_arg (unsigned int i) { - blend_arg_t &arg = argStack[i]; - blend_arg (arg); - return arg; + return SUPER::argStack[i]; } - const blend_arg_t& pop_arg () + const ELEM& pop_arg () { - blend_arg_t &arg = argStack.pop (); - blend_arg (arg); - return arg; + return SUPER::argStack.pop (); } void process_blend () @@ -122,7 +119,7 @@ if (do_blend) { if (unlikely (!scalars.resize (region_count))) - set_error (); + SUPER::set_error (); else varStore->varStore.get_region_scalars (get_ivs (), coords, num_coords, &scalars[0], region_count); @@ -133,10 +130,10 @@ void process_vsindex () { - unsigned int index = argStack.pop_uint (); + unsigned int index = SUPER::argStack.pop_uint (); if (unlikely (seen_vsindex () || seen_blend)) { - set_error (); + SUPER::set_error (); } else { @@ -151,22 +148,18 @@ void set_ivs (unsigned int ivs_) { ivs = ivs_; } bool seen_vsindex () const { return seen_vsindex_; } - protected: - void blend_arg (blend_arg_t &arg) + double blend_deltas (hb_array_t<const ELEM> deltas) const { - if (do_blend && arg.blending ()) + double v = 0; + if (do_blend) { - if (likely (scalars.length == arg.deltas.length)) + if (likely (scalars.length == deltas.length)) { - double v = arg.to_real (); for (unsigned int i = 0; i < scalars.length; i++) - { - v += (double)scalars[i] * arg.deltas[i].to_real (); - } - arg.set_real (v); - arg.deltas.resize (0); + v += (double) scalars[i] * deltas[i].to_real (); } } + return v; } protected: @@ -180,22 +173,24 @@ bool seen_vsindex_; bool seen_blend; - typedef cs_interp_env_t<blend_arg_t, CFF2Subrs> SUPER; + typedef cs_interp_env_t<ELEM, CFF2Subrs> SUPER; }; -template <typename OPSET, typename PARAM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t, PARAM>> -struct cff2_cs_opset_t : cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH> +template <typename OPSET, typename PARAM, typename ELEM, typename PATH=path_procs_null_t<cff2_cs_interp_env_t<ELEM>, PARAM>> +struct cff2_cs_opset_t : cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH> { - static void process_op (op_code_t op, cff2_cs_interp_env_t &env, PARAM& param) + static void process_op (op_code_t op, cff2_cs_interp_env_t<ELEM> &env, PARAM& param) { switch (op) { case OpCode_callsubr: case OpCode_callgsubr: /* a subroutine number shouldn't be a blended value */ +#if 0 if (unlikely (env.argStack.peek ().blending ())) { env.set_error (); break; } +#endif SUPER::process_op (op, env, param); break; @@ -204,11 +199,13 @@ break; case OpCode_vsindexcs: +#if 0 if (unlikely (env.argStack.peek ().blending ())) { env.set_error (); break; } +#endif OPSET::process_vsindex (env, param); break; @@ -217,7 +214,26 @@ } } - static void process_blend (cff2_cs_interp_env_t &env, PARAM& param) + template <typename T = ELEM, + hb_enable_if (hb_is_same (T, blend_arg_t))> + static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env, + ELEM &arg, + const hb_array_t<const ELEM> blends, + unsigned n, unsigned i) + { + arg.set_blends (n, i, blends.length, blends); + } + template <typename T = ELEM, + hb_enable_if (!hb_is_same (T, blend_arg_t))> + static void process_arg_blend (cff2_cs_interp_env_t<ELEM> &env, + ELEM &arg, + const hb_array_t<const ELEM> blends, + unsigned n, unsigned i) + { + arg.set_real (arg.to_real () + env.blend_deltas (blends)); + } + + static void process_blend (cff2_cs_interp_env_t<ELEM> &env, PARAM& param) { unsigned int n, k; @@ -234,26 +250,26 @@ } for (unsigned int i = 0; i < n; i++) { - const hb_array_t<const blend_arg_t> blends = env.argStack.get_subarray (start + n + (i * k)); - env.argStack[start + i].set_blends (n, i, k, blends); + const hb_array_t<const ELEM> blends = env.argStack.sub_array (start + n + (i * k), k); + process_arg_blend (env, env.argStack[start + i], blends, n, i); } /* pop off blend values leaving default values now adorned with blend values */ env.argStack.pop (k * n); } - static void process_vsindex (cff2_cs_interp_env_t &env, PARAM& param) + static void process_vsindex (cff2_cs_interp_env_t<ELEM> &env, PARAM& param) { env.process_vsindex (); env.clear_args (); } private: - typedef cs_opset_t<blend_arg_t, OPSET, cff2_cs_interp_env_t, PARAM, PATH> SUPER; + typedef cs_opset_t<ELEM, OPSET, cff2_cs_interp_env_t<ELEM>, PARAM, PATH> SUPER; }; -template <typename OPSET, typename PARAM> -struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t, OPSET, PARAM> {}; +template <typename OPSET, typename PARAM, typename ELEM> +struct cff2_cs_interpreter_t : cs_interpreter_t<cff2_cs_interp_env_t<ELEM>, OPSET, PARAM> {}; } /* namespace CFF */
diff --git a/src/hb-ot-cff2-table.cc b/src/hb-ot-cff2-table.cc index 7849c5a..0c74f9b 100644 --- a/src/hb-ot-cff2-table.cc +++ b/src/hb-ot-cff2-table.cc
@@ -64,15 +64,15 @@ number_t max_y; }; -struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t, cff2_extents_param_t> +struct cff2_path_procs_extents_t : path_procs_t<cff2_path_procs_extents_t, cff2_cs_interp_env_t<number_t>, cff2_extents_param_t> { - static void moveto (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt) + static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt) { param.end_path (); env.moveto (pt); } - static void line (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1) + static void line (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1) { if (!param.is_path_open ()) { @@ -83,7 +83,7 @@ param.update_bounds (env.get_pt ()); } - static void curve (cff2_cs_interp_env_t &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_extents_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) { if (!param.is_path_open ()) { @@ -98,7 +98,7 @@ } }; -struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, cff2_path_procs_extents_t> {}; +struct cff2_cs_opset_extents_t : cff2_cs_opset_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t, cff2_path_procs_extents_t> {}; bool OT::cff2::accelerator_t::get_extents (hb_font_t *font, hb_codepoint_t glyph, @@ -112,7 +112,7 @@ if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; unsigned int fd = fdSelect->get_fd (glyph); - cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t> interp; + cff2_cs_interpreter_t<cff2_cs_opset_extents_t, cff2_extents_param_t, number_t> interp; const hb_ubytes_t str = (*charStrings)[glyph]; interp.env.init (str, *this, fd, font->coords, font->num_coords); cff2_extents_param_t param; @@ -169,28 +169,28 @@ hb_font_t *font; }; -struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t, cff2_path_param_t> +struct cff2_path_procs_path_t : path_procs_t<cff2_path_procs_path_t, cff2_cs_interp_env_t<number_t>, cff2_path_param_t> { - static void moveto (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt) + static void moveto (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt) { param.move_to (pt); env.moveto (pt); } - static void line (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1) + static void line (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1) { param.line_to (pt1); env.moveto (pt1); } - static void curve (cff2_cs_interp_env_t &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) + static void curve (cff2_cs_interp_env_t<number_t> &env, cff2_path_param_t& param, const point_t &pt1, const point_t &pt2, const point_t &pt3) { param.cubic_to (pt1, pt2, pt3); env.moveto (pt3); } }; -struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, cff2_path_procs_path_t> {}; +struct cff2_cs_opset_path_t : cff2_cs_opset_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t, cff2_path_procs_path_t> {}; bool OT::cff2::accelerator_t::get_path (hb_font_t *font, hb_codepoint_t glyph, hb_draw_session_t &draw_session) const { @@ -202,7 +202,7 @@ if (unlikely (!is_valid () || (glyph >= num_glyphs))) return false; unsigned int fd = fdSelect->get_fd (glyph); - cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t> interp; + cff2_cs_interpreter_t<cff2_cs_opset_path_t, cff2_path_param_t, number_t> interp; const hb_ubytes_t str = (*charStrings)[glyph]; interp.env.init (str, *this, fd, font->coords, font->num_coords); cff2_path_param_t param (font, draw_session);
diff --git a/src/hb-subset-cff2.cc b/src/hb-subset-cff2.cc index 92dd6b1..08e820e 100644 --- a/src/hb-subset-cff2.cc +++ b/src/hb-subset-cff2.cc
@@ -67,9 +67,9 @@ } }; -struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> +struct cff2_cs_opset_flatten_t : cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> { - static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) + static void flush_args_and_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) { switch (op) { @@ -97,7 +97,7 @@ } } - static void flush_args (cff2_cs_interp_env_t &env, flatten_param_t& param) + static void flush_args (cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) { for (unsigned int i = 0; i < env.argStack.get_count ();) { @@ -122,7 +122,7 @@ SUPER::flush_args (env, param); } - static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t &env, flatten_param_t& param) + static void flatten_blends (const blend_arg_t &arg, unsigned int i, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) { /* flatten the default values */ str_encoder_t encoder (param.flatStr); @@ -149,7 +149,7 @@ encoder.encode_op (OpCode_blendcs); } - static void flush_op (op_code_t op, cff2_cs_interp_env_t &env, flatten_param_t& param) + static void flush_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, flatten_param_t& param) { switch (op) { @@ -163,13 +163,13 @@ } private: - typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t> SUPER; - typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t, flatten_param_t> CSOPSET; + typedef cff2_cs_opset_t<cff2_cs_opset_flatten_t, flatten_param_t, blend_arg_t> SUPER; + typedef cs_opset_t<blend_arg_t, cff2_cs_opset_flatten_t, cff2_cs_opset_flatten_t, cff2_cs_interp_env_t<blend_arg_t>, flatten_param_t> CSOPSET; }; -struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> +struct cff2_cs_opset_subr_subset_t : cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> { - static void process_op (op_code_t op, cff2_cs_interp_env_t &env, subr_subset_param_t& param) + static void process_op (op_code_t op, cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param) { switch (op) { @@ -201,7 +201,7 @@ protected: static void process_call_subr (op_code_t op, cs_type_t type, - cff2_cs_interp_env_t &env, subr_subset_param_t& param, + cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, cff2_biased_subrs_t& subrs, hb_set_t *closure) { byte_str_ref_t str_ref = env.str_ref; @@ -212,15 +212,15 @@ } private: - typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t> SUPER; + typedef cff2_cs_opset_t<cff2_cs_opset_subr_subset_t, subr_subset_param_t, blend_arg_t> SUPER; }; -struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_subr_subset_t> +struct cff2_subr_subsetter_t : subr_subsetter_t<cff2_subr_subsetter_t, CFF2Subrs, const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_subr_subset_t> { cff2_subr_subsetter_t (const OT::cff2::accelerator_subset_t &acc_, const hb_subset_plan_t *plan_) : subr_subsetter_t (acc_, plan_) {} - static void complete_parsed_str (cff2_cs_interp_env_t &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) + static void complete_parsed_str (cff2_cs_interp_env_t<blend_arg_t> &env, subr_subset_param_t& param, parsed_cs_str_t &charstring) { /* vsindex is inserted at the beginning of the charstring as necessary */ if (env.seen_vsindex ()) @@ -245,7 +245,7 @@ if (desubroutinize) { /* Flatten global & local subrs */ - subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t, cff2_cs_opset_flatten_t> + subr_flattener_t<const OT::cff2::accelerator_subset_t, cff2_cs_interp_env_t<blend_arg_t>, cff2_cs_opset_flatten_t> flattener(acc, plan); if (!flattener.flatten (subset_charstrings)) return false;