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