[VARC] Add loads of debug messages and misc changes

To be reverted.
diff --git a/src/OT/Var/VARC/VARC.cc b/src/OT/Var/VARC/VARC.cc
index 7524686..888968c 100644
--- a/src/OT/Var/VARC/VARC.cc
+++ b/src/OT/Var/VARC/VARC.cc
@@ -10,6 +10,28 @@
 
 //namespace Var {
 
+static constexpr uint32_t DEBUG_VARC_TRACE_VAR_IDX = 22478849;
+
+static void
+debug_trace_coords (hb_codepoint_t parent_gid,
+                    hb_codepoint_t gid,
+                    uint32_t var_idx,
+                    hb_array_t<const float> coords_f32)
+{
+  if (var_idx != DEBUG_VARC_TRACE_VAR_IDX)
+    return;
+
+  fprintf (stderr,
+           "VARC_COORDS parent_gid=%u gid=%u var_idx=%u len=%u",
+           (unsigned) parent_gid,
+           (unsigned) gid,
+           (unsigned) var_idx,
+           (unsigned) coords_f32.length);
+  for (unsigned i = 0; i < coords_f32.length; i++)
+    fprintf (stderr, " %u:%.16f/%d", i, (double) coords_f32[i], (int) roundf (coords_f32[i]));
+  fprintf (stderr, "\n");
+}
+
 
 #ifndef HB_NO_DRAW
 
@@ -19,9 +41,164 @@
   hb_draw_funcs_t *dfuncs;
   void *data;
   hb_draw_state_t *st;
+  float draw_x_scale;
+  float draw_y_scale;
+  bool trace_commands;
+  hb_codepoint_t parent_gid;
+  hb_codepoint_t gid;
+  uint32_t var_idx;
+  unsigned seq;
 };
 
 static void
+debug_trace_cmd_point (const hb_transforming_pen_context_t *c,
+                       const char *phase,
+                       unsigned seq,
+                       const char *op,
+                       float x,
+                       float y)
+{
+  if (!c->trace_commands)
+    return;
+
+  fprintf (stderr,
+           "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s x_raw_26_6=%.16f y_raw_26_6=%.16f x_norm=%.16f y_norm=%.16f x_cmp=%.16f y_cmp=%.16f x_after_draw=%.16f y_after_draw=%.16f x_unscaled=%.16f y_unscaled=%.16f\n",
+           phase,
+           (unsigned) c->parent_gid,
+           (unsigned) c->gid,
+           (unsigned) c->var_idx,
+           seq,
+           op,
+           (double) (x * 64.0f),
+           (double) (y * 64.0f),
+           (double) x,
+           (double) y,
+           (double) x,
+           (double) y,
+           (double) (x * c->draw_x_scale),
+           (double) (y * c->draw_y_scale),
+           (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f));
+}
+
+static void
+debug_trace_cmd_quad (const hb_transforming_pen_context_t *c,
+                      const char *phase,
+                      unsigned seq,
+                      const char *op,
+                      float cx0,
+                      float cy0,
+                      float x,
+                      float y)
+{
+  if (!c->trace_commands)
+    return;
+
+  fprintf (stderr,
+           "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s cx0_raw_26_6=%.16f cy0_raw_26_6=%.16f x_raw_26_6=%.16f y_raw_26_6=%.16f cx0_norm=%.16f cy0_norm=%.16f x_norm=%.16f y_norm=%.16f cx0_cmp=%.16f cy0_cmp=%.16f x_cmp=%.16f y_cmp=%.16f cx0_after_draw=%.16f cy0_after_draw=%.16f x_after_draw=%.16f y_after_draw=%.16f cx0_unscaled=%.16f cy0_unscaled=%.16f x_unscaled=%.16f y_unscaled=%.16f\n",
+           phase,
+           (unsigned) c->parent_gid,
+           (unsigned) c->gid,
+           (unsigned) c->var_idx,
+           seq,
+           op,
+           (double) (cx0 * 64.0f),
+           (double) (cy0 * 64.0f),
+           (double) (x * 64.0f),
+           (double) (y * 64.0f),
+           (double) cx0,
+           (double) cy0,
+           (double) x,
+           (double) y,
+           (double) cx0,
+           (double) cy0,
+           (double) x,
+           (double) y,
+           (double) (cx0 * c->draw_x_scale),
+           (double) (cy0 * c->draw_y_scale),
+           (double) (x * c->draw_x_scale),
+           (double) (y * c->draw_y_scale),
+           (double) (c->draw_x_scale ? (cx0 / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (cy0 / c->draw_y_scale) : 0.f),
+           (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f));
+}
+
+static void
+debug_trace_cmd_cubic (const hb_transforming_pen_context_t *c,
+                       const char *phase,
+                       unsigned seq,
+                       const char *op,
+                       float cx0,
+                       float cy0,
+                       float cx1,
+                       float cy1,
+                       float x,
+                       float y)
+{
+  if (!c->trace_commands)
+    return;
+
+  fprintf (stderr,
+           "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s cx0_raw_26_6=%.16f cy0_raw_26_6=%.16f cx1_raw_26_6=%.16f cy1_raw_26_6=%.16f x_raw_26_6=%.16f y_raw_26_6=%.16f cx0_norm=%.16f cy0_norm=%.16f cx1_norm=%.16f cy1_norm=%.16f x_norm=%.16f y_norm=%.16f cx0_cmp=%.16f cy0_cmp=%.16f cx1_cmp=%.16f cy1_cmp=%.16f x_cmp=%.16f y_cmp=%.16f cx0_after_draw=%.16f cy0_after_draw=%.16f cx1_after_draw=%.16f cy1_after_draw=%.16f x_after_draw=%.16f y_after_draw=%.16f cx0_unscaled=%.16f cy0_unscaled=%.16f cx1_unscaled=%.16f cy1_unscaled=%.16f x_unscaled=%.16f y_unscaled=%.16f\n",
+           phase,
+           (unsigned) c->parent_gid,
+           (unsigned) c->gid,
+           (unsigned) c->var_idx,
+           seq,
+           op,
+           (double) (cx0 * 64.0f),
+           (double) (cy0 * 64.0f),
+           (double) (cx1 * 64.0f),
+           (double) (cy1 * 64.0f),
+           (double) (x * 64.0f),
+           (double) (y * 64.0f),
+           (double) cx0,
+           (double) cy0,
+           (double) cx1,
+           (double) cy1,
+           (double) x,
+           (double) y,
+           (double) cx0,
+           (double) cy0,
+           (double) cx1,
+           (double) cy1,
+           (double) x,
+           (double) y,
+           (double) (cx0 * c->draw_x_scale),
+           (double) (cy0 * c->draw_y_scale),
+           (double) (cx1 * c->draw_x_scale),
+           (double) (cy1 * c->draw_y_scale),
+           (double) (x * c->draw_x_scale),
+           (double) (y * c->draw_y_scale),
+           (double) (c->draw_x_scale ? (cx0 / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (cy0 / c->draw_y_scale) : 0.f),
+           (double) (c->draw_x_scale ? (cx1 / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (cy1 / c->draw_y_scale) : 0.f),
+           (double) (c->draw_x_scale ? (x / c->draw_x_scale) : 0.f),
+           (double) (c->draw_y_scale ? (y / c->draw_y_scale) : 0.f));
+}
+
+static void
+debug_trace_cmd_close (const hb_transforming_pen_context_t *c,
+                       const char *phase,
+                       unsigned seq,
+                       const char *op)
+{
+  if (!c->trace_commands)
+    return;
+
+  fprintf (stderr,
+           "VARC_CMD phase=%s parent_gid=%u gid=%u var_idx=%u seq=%u op=%s\n",
+           phase,
+           (unsigned) c->parent_gid,
+           (unsigned) c->gid,
+           (unsigned) c->var_idx,
+           seq,
+           op);
+}
+
+static void
 hb_transforming_pen_move_to (hb_draw_funcs_t *dfuncs HB_UNUSED,
 			     void *data,
 			     hb_draw_state_t *st,
@@ -29,8 +206,13 @@
 			     void *user_data HB_UNUSED)
 {
   hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
+  unsigned seq = c->seq++;
+  float pre_x = to_x;
+  float pre_y = to_y;
+  debug_trace_cmd_point (c, "pre", seq, "M", pre_x, pre_y);
 
   c->transform.transform_point (to_x, to_y);
+  debug_trace_cmd_point (c, "post", seq, "M", to_x, to_y);
 
   c->dfuncs->move_to (c->data, *c->st, to_x, to_y);
 }
@@ -43,8 +225,13 @@
 			     void *user_data HB_UNUSED)
 {
   hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
+  unsigned seq = c->seq++;
+  float pre_x = to_x;
+  float pre_y = to_y;
+  debug_trace_cmd_point (c, "pre", seq, "L", pre_x, pre_y);
 
   c->transform.transform_point (to_x, to_y);
+  debug_trace_cmd_point (c, "post", seq, "L", to_x, to_y);
 
   c->dfuncs->line_to (c->data, *c->st, to_x, to_y);
 }
@@ -58,9 +245,16 @@
 				  void *user_data HB_UNUSED)
 {
   hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
+  unsigned seq = c->seq++;
+  float pre_control_x = control_x;
+  float pre_control_y = control_y;
+  float pre_to_x = to_x;
+  float pre_to_y = to_y;
+  debug_trace_cmd_quad (c, "pre", seq, "Q", pre_control_x, pre_control_y, pre_to_x, pre_to_y);
 
   c->transform.transform_point (control_x, control_y);
   c->transform.transform_point (to_x, to_y);
+  debug_trace_cmd_quad (c, "post", seq, "Q", control_x, control_y, to_x, to_y);
 
   c->dfuncs->quadratic_to (c->data, *c->st, control_x, control_y, to_x, to_y);
 }
@@ -75,10 +269,25 @@
 			      void *user_data HB_UNUSED)
 {
   hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
+  unsigned seq = c->seq++;
+  float pre_control1_x = control1_x;
+  float pre_control1_y = control1_y;
+  float pre_control2_x = control2_x;
+  float pre_control2_y = control2_y;
+  float pre_to_x = to_x;
+  float pre_to_y = to_y;
+  debug_trace_cmd_cubic (c, "pre", seq, "C",
+                         pre_control1_x, pre_control1_y,
+                         pre_control2_x, pre_control2_y,
+                         pre_to_x, pre_to_y);
 
   c->transform.transform_point (control1_x, control1_y);
   c->transform.transform_point (control2_x, control2_y);
   c->transform.transform_point (to_x, to_y);
+  debug_trace_cmd_cubic (c, "post", seq, "C",
+                         control1_x, control1_y,
+                         control2_x, control2_y,
+                         to_x, to_y);
 
   c->dfuncs->cubic_to (c->data, *c->st, control1_x, control1_y, control2_x, control2_y, to_x, to_y);
 }
@@ -90,8 +299,11 @@
 				void *user_data HB_UNUSED)
 {
   hb_transforming_pen_context_t *c = (hb_transforming_pen_context_t *) data;
+  unsigned seq = c->seq++;
+  debug_trace_cmd_close (c, "pre", seq, "Z");
 
   c->dfuncs->close_path (c->data, *c->st);
+  debug_trace_cmd_close (c, "post", seq, "Z");
 }
 
 static inline void free_static_transforming_pen_funcs ();
@@ -131,10 +343,11 @@
 hb_ubytes_t
 VarComponent::get_path_at (const hb_varc_context_t &c,
 			   hb_codepoint_t parent_gid,
-			   hb_array_t<const int> coords,
+			   hb_array_t<const float> coords_f32,
 			   hb_transform_t<> total_transform,
 			   hb_ubytes_t total_record,
-			   hb_scalar_cache_t *cache) const
+			   hb_scalar_cache_t *cache,
+			   uint32_t trace_var_idx) const
 {
   const unsigned char *end = total_record.arrayZ + total_record.length;
   const unsigned char *record = total_record.arrayZ;
@@ -180,11 +393,18 @@
   bool show = true;
   if (flags & (unsigned) flags_t::HAVE_CONDITION)
   {
+    hb_vector_t<int> condition_coords_storage;
+    if (coords_f32.length && unlikely (!condition_coords_storage.resize (coords_f32.length)))
+      return hb_ubytes_t ();
+    for (unsigned i = 0; i < coords_f32.length; i++)
+      condition_coords_storage[i] = (int) roundf (coords_f32[i]);
+    hb_array_t<const int> condition_coords = condition_coords_storage.as_array ();
+
     unsigned conditionIndex;
     READ_UINT32VAR (conditionIndex);
     const auto &condition = (&VARC+VARC.conditionList)[conditionIndex];
-    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, coords, cache);
-    show = condition.evaluate (coords.arrayZ, coords.length, &instancer);
+    auto instancer = MultiItemVarStoreInstancer(&varStore, nullptr, condition_coords, coords_f32, cache);
+    show = condition.evaluate (condition_coords.arrayZ, condition_coords.length, &instancer);
   }
 
   // Axis values
@@ -209,16 +429,24 @@
   {
     uint32_t axisValuesVarIdx;
     READ_UINT32VAR (axisValuesVarIdx);
-    if (show && coords && !axisValues.in_error ())
-      varStore.get_delta (axisValuesVarIdx, coords, axisValues.as_array (), cache);
+    if (show && coords_f32 && !axisValues.in_error ())
+      varStore.get_delta (axisValuesVarIdx, coords_f32, axisValues.as_array (), cache);
   }
 
-  auto component_coords = coords;
+  hb_vector_t<float> component_coords_f32_storage;
+  hb_array_t<const float> component_coords_f32 = coords_f32;
+
   /* Copying coords is expensive; so we have put an arbitrary
    * limit on the max number of coords for now. */
   if ((flags & (unsigned) flags_t::RESET_UNSPECIFIED_AXES) ||
-      coords.length > HB_VAR_COMPOSITE_MAX_AXES)
-    component_coords = hb_array (c.font->coords, c.font->num_coords);
+      component_coords_f32.length > HB_VAR_COMPOSITE_MAX_AXES)
+  {
+    if (unlikely (!component_coords_f32_storage.resize (c.font->num_coords)))
+      return hb_ubytes_t ();
+    for (unsigned i = 0; i < c.font->num_coords; i++)
+      component_coords_f32_storage[i] = (float) c.font->coords[i];
+    component_coords_f32 = component_coords_f32_storage.as_array ();
+  }
 
   // Transform
 
@@ -268,30 +496,94 @@
 
   if (show)
   {
-    // Only use coord_setter if there's actually any axis overrides.
-    coord_setter_t coord_setter (axisIndices ? component_coords : hb_array<int> ());
-    for (unsigned i = 0; i < axisIndices.length; i++)
-      coord_setter[axisIndices[i]] = roundf (axisValues[i]);
+    // Apply axis overrides in float coordinate space.
     if (axisIndices)
-      component_coords = coord_setter.get_coords ();
+    {
+      if (component_coords_f32.arrayZ != component_coords_f32_storage.arrayZ)
+      {
+        component_coords_f32_storage.clear ();
+        component_coords_f32_storage.extend (component_coords_f32);
+        if (unlikely (component_coords_f32_storage.in_error ()))
+          return hb_ubytes_t ();
+        component_coords_f32 = component_coords_f32_storage.as_array ();
+      }
+
+      for (unsigned i = 0; i < axisIndices.length; i++)
+      {
+        unsigned axis = axisIndices[i];
+        if (axis >= HB_VAR_COMPOSITE_MAX_AXES)
+          continue;
+        if (unlikely (component_coords_f32_storage.length <= axis &&
+                      !component_coords_f32_storage.resize (axis + 1)))
+          return hb_ubytes_t ();
+        component_coords_f32_storage[axis] = axisValues[i];
+      }
+      component_coords_f32 = component_coords_f32_storage.as_array ();
+    }
 
     // Apply transform variations if any
-    if (transformVarIdx != VarIdx::NO_VARIATION && coords)
+    if (transformVarIdx != VarIdx::NO_VARIATION && coords_f32)
     {
+      debug_trace_coords (parent_gid, gid, transformVarIdx, coords_f32);
+
+      float rotation_pre_raw = transform.rotation;
       float transformValues[9];
+      unsigned rotationIndex = (unsigned) -1;
+      if (flags & (unsigned) flags_t::HAVE_ROTATION)
+      {
+        rotationIndex = 0;
+        if (flags & (unsigned) flags_t::HAVE_TRANSLATE_X) rotationIndex++;
+        if (flags & (unsigned) flags_t::HAVE_TRANSLATE_Y) rotationIndex++;
+      }
       unsigned numTransformValues = 0;
 #define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
 	  if (flags & (unsigned) flags_t::flag) \
 	    transformValues[numTransformValues++] = transform.name;
       PROCESS_TRANSFORM_COMPONENTS;
 #undef PROCESS_TRANSFORM_COMPONENT
-      varStore.get_delta (transformVarIdx, coords, hb_array (transformValues, numTransformValues), cache);
+      varStore.get_delta (transformVarIdx, coords_f32, hb_array (transformValues, numTransformValues), cache);
+      if (rotationIndex < numTransformValues)
+        varStore.debug_rotation_region_terms (transformVarIdx,
+                                              coords_f32.arrayZ, coords_f32.length,
+                                              numTransformValues,
+                                              rotationIndex,
+                                              parent_gid,
+                                              gid,
+                                              cache);
       numTransformValues = 0;
 #define PROCESS_TRANSFORM_COMPONENT(shift, type, flag, name) \
 	  if (flags & (unsigned) flags_t::flag) \
 	    transform.name = transformValues[numTransformValues++];
       PROCESS_TRANSFORM_COMPONENTS;
 #undef PROCESS_TRANSFORM_COMPONENT
+      if (rotationIndex != (unsigned) -1)
+        fprintf (stderr,
+                 "VARC_ROT parent_gid=%u gid=%u var_idx=%u pre_raw=%.16f pre=%.16f delta_raw=%.16f delta=%.16f post_raw=%.16f post=%.16f\n",
+                 (unsigned) parent_gid,
+                 (unsigned) gid,
+                 (unsigned) transformVarIdx,
+                 (double) rotation_pre_raw,
+                 (double) (rotation_pre_raw * (1.f / (1 << 12))),
+                 (double) (transform.rotation - rotation_pre_raw),
+                 (double) ((transform.rotation - rotation_pre_raw) * (1.f / (1 << 12))),
+                 (double) transform.rotation,
+                 (double) (transform.rotation * (1.f / (1 << 12))));
+
+      if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX)
+        fprintf (stderr,
+                 "VARC_XFORM parent_gid=%u gid=%u var_idx=%u tx=%.16f ty=%.16f rot_raw=%.16f sx_raw=%.16f sy_raw=%.16f skx_raw=%.16f sky_raw=%.16f cx=%.16f cy=%.16f\n",
+                 (unsigned) parent_gid,
+                 (unsigned) gid,
+                 (unsigned) transformVarIdx,
+                 (double) transform.translateX,
+                 (double) transform.translateY,
+                 (double) transform.rotation,
+                 (double) transform.scaleX,
+                 (double) transform.scaleY,
+                 (double) transform.skewX,
+                 (double) transform.skewY,
+                 (double) transform.tCenterX,
+                 (double) transform.tCenterY);
     }
 
     // Divide them by their divisors
@@ -308,16 +600,61 @@
     transform.skewX *= HB_PI;
     transform.skewY *= HB_PI;
 
-    total_transform.transform (transform.to_transform ());
+    if (transform.rotation != 0)
+    {
+      float s, c;
+      hb_sincos (transform.rotation, s, c);
+      fprintf (stderr,
+               "VARC_TRIG kind=rotate in=%.16f sin=%.16f cos=%.16f\n",
+               (double) transform.rotation,
+               (double) s,
+               (double) c);
+    }
+    if (transform.skewX != 0 || transform.skewY != 0)
+    {
+      float skew_x = -transform.skewX;
+      float skew_y = transform.skewY;
+      float tan_x = skew_x ? tanf (skew_x) : 0;
+      float tan_y = skew_y ? tanf (skew_y) : 0;
+      fprintf (stderr,
+               "VARC_TRIG kind=skew in_x=%.16f in_y=%.16f tan_x=%.16f tan_y=%.16f\n",
+               (double) skew_x,
+               (double) skew_y,
+               (double) tan_x,
+               (double) tan_y);
+    }
 
-    bool same_coords = component_coords.length == coords.length &&
-		       component_coords.arrayZ == coords.arrayZ;
+    hb_transform_t<> local_transform = transform.to_transform ();
+    hb_transform_t<> combined_transform = total_transform;
+    combined_transform.transform (local_transform);
+    if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX)
+      fprintf (stderr,
+               "VARC_AFFINE parent_gid=%u gid=%u var_idx=%u xx=%.16f yx=%.16f xy=%.16f yy=%.16f x0=%.16f y0=%.16f\n",
+               (unsigned) parent_gid,
+               (unsigned) gid,
+               (unsigned) transformVarIdx,
+               (double) combined_transform.xx,
+               (double) combined_transform.yx,
+               (double) combined_transform.xy,
+               (double) combined_transform.yy,
+               (double) combined_transform.x0,
+               (double) combined_transform.y0);
+    total_transform = combined_transform;
+
+    uint32_t child_trace_var_idx = trace_var_idx;
+    if (transformVarIdx == DEBUG_VARC_TRACE_VAR_IDX)
+      child_trace_var_idx = transformVarIdx;
+
+    bool same_coords = component_coords_f32.length == coords_f32.length &&
+		       component_coords_f32.arrayZ == coords_f32.arrayZ;
 
     c.depth_left--;
     VARC.get_path_at (c, gid,
-		      component_coords, total_transform,
+		      component_coords_f32,
+		      total_transform,
 		      parent_gid,
-		      same_coords ? cache : nullptr);
+		      same_coords ? cache : nullptr,
+                      child_trace_var_idx);
     c.depth_left++;
   }
 
@@ -330,10 +667,11 @@
 bool
 VARC::get_path_at (const hb_varc_context_t &c,
 		   hb_codepoint_t glyph,
-		   hb_array_t<const int> coords,
+		   hb_array_t<const float> coords_f32,
 		   hb_transform_t<> transform,
 		   hb_codepoint_t parent_glyph,
-		   hb_scalar_cache_t *parent_cache) const
+		   hb_scalar_cache_t *parent_cache,
+                   uint32_t trace_var_idx) const
 {
   // Don't recurse on the same glyph.
   unsigned idx = glyph == parent_glyph ?
@@ -341,20 +679,60 @@
 		 (this+coverage).get_coverage (glyph);
   if (idx == NOT_COVERED)
   {
+    hb_vector_t<int> coords_storage;
+    if (coords_f32.length && unlikely (!coords_storage.resize (coords_f32.length)))
+      return false;
+    for (unsigned i = 0; i < coords_f32.length; i++)
+      coords_storage[i] = (int) roundf (coords_f32[i]);
+    hb_array_t<const int> coords = coords_storage.as_array ();
+
     if (c.draw_session)
     {
       hb_transform_t<> leaf_transform = transform;
       leaf_transform.x0 *= c.font->x_multf;
       leaf_transform.y0 *= c.font->y_multf;
+      bool trace_commands = trace_var_idx == DEBUG_VARC_TRACE_VAR_IDX;
+      float draw_x_scale = 1.f;
+      float draw_y_scale = 1.f;
+      if (c.font && c.font->parent)
+      {
+        if (c.font->parent->x_scale)
+          draw_x_scale = (float) c.font->x_scale / (float) c.font->parent->x_scale;
+        if (c.font->parent->y_scale)
+          draw_y_scale = (float) c.font->y_scale / (float) c.font->parent->y_scale;
+      }
+      if (trace_commands)
+      {
+        fprintf (stderr,
+                 "VARC_CMD_CTX parent_gid=%u gid=%u var_idx=%u draw_x_scale=%.16f draw_y_scale=%.16f font_x_scale=%d font_y_scale=%d parent_x_scale=%d parent_y_scale=%d\n",
+                 (unsigned) parent_glyph,
+                 (unsigned) glyph,
+                 (unsigned) trace_var_idx,
+                 (double) draw_x_scale,
+                 (double) draw_y_scale,
+                 c.font ? c.font->x_scale : 0,
+                 c.font ? c.font->y_scale : 0,
+                 (c.font && c.font->parent) ? c.font->parent->x_scale : 0,
+                 (c.font && c.font->parent) ? c.font->parent->y_scale : 0);
+      }
 
       // 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 {leaf_transform,
 					     c.draw_session->funcs,
 					     c.draw_session->draw_data,
-					     &c.draw_session->st};
+					     &c.draw_session->st,
+                                             draw_x_scale,
+                                             draw_y_scale,
+                                             trace_commands,
+                                             parent_glyph,
+                                             glyph,
+                                             trace_var_idx,
+                                             0};
       hb_draw_session_t transformer_session {transformer_funcs, &context};
-      hb_draw_session_t &shape_draw_session = leaf_transform.is_identity () ? *c.draw_session : transformer_session;
+      hb_draw_session_t &shape_draw_session = (!trace_commands && leaf_transform.is_identity ())
+                                              ? *c.draw_session
+                                              : transformer_session;
 
       if (c.font->face->table.glyf->get_path_at (c.font, glyph, shape_draw_session, coords, c.scratch.glyf_scratch)) return true;
 #ifndef HB_NO_CFF
@@ -398,14 +776,16 @@
 
   hb_scalar_cache_t static_cache;
   hb_scalar_cache_t *cache = parent_cache ?
-				  parent_cache :
-				  (this+varStore).create_cache (&static_cache);
+                                  parent_cache :
+                                  (this+varStore).create_cache (&static_cache);
 
   VarCompositeGlyph::get_path_at (c,
-				  glyph,
-				  coords, transform,
-				  record,
-				  cache);
+                                  glyph,
+                                  coords_f32,
+                                  transform,
+                                  record,
+                                  cache,
+                                  trace_var_idx);
 
   if (cache != parent_cache)
     (this+varStore).destroy_cache (cache, &static_cache);
diff --git a/src/OT/Var/VARC/VARC.hh b/src/OT/Var/VARC/VARC.hh
index 719302c..83a2806 100644
--- a/src/OT/Var/VARC/VARC.hh
+++ b/src/OT/Var/VARC/VARC.hh
@@ -8,7 +8,6 @@
 #include "../../../hb-ot-cff2-table.hh"
 #include "../../../hb-ot-cff1-table.hh"
 
-#include "coord-setter.hh"
 
 namespace OT {
 
@@ -64,10 +63,11 @@
   HB_INTERNAL hb_ubytes_t
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t parent_gid,
-	       hb_array_t<const int> coords,
+	       hb_array_t<const float> coords_f32,
 	       hb_transform_t<> transform,
 	       hb_ubytes_t record,
-	       hb_scalar_cache_t *cache = nullptr) const;
+	       hb_scalar_cache_t *cache = nullptr,
+	       uint32_t trace_var_idx = VarIdx::NO_VARIATION) const;
 };
 
 struct VarCompositeGlyph
@@ -75,19 +75,21 @@
   static void
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t gid,
-	       hb_array_t<const int> coords,
+	       hb_array_t<const float> coords_f32,
 	       hb_transform_t<> transform,
 	       hb_ubytes_t record,
-	       hb_scalar_cache_t *cache)
+	       hb_scalar_cache_t *cache,
+	       uint32_t trace_var_idx)
   {
     while (record)
     {
       const VarComponent &comp = * (const VarComponent *) (record.arrayZ);
       record = comp.get_path_at (c,
 				 gid,
-				 coords, transform,
+				 coords_f32, transform,
 				 record,
-				 cache);
+				 cache,
+				 trace_var_idx);
     }
   }
 };
@@ -103,10 +105,11 @@
   HB_INTERNAL bool
   get_path_at (const hb_varc_context_t &c,
 	       hb_codepoint_t gid,
-	       hb_array_t<const int> coords,
+	       hb_array_t<const float> coords_f32,
 	       hb_transform_t<> transform = HB_TRANSFORM_IDENTITY,
 	       hb_codepoint_t parent_gid = HB_CODEPOINT_INVALID,
-	       hb_scalar_cache_t *parent_cache = nullptr) const;
+	       hb_scalar_cache_t *parent_cache = nullptr,
+	       uint32_t trace_var_idx = VarIdx::NO_VARIATION) const;
 
   bool
   get_path (hb_font_t *font,
@@ -122,8 +125,14 @@
 			 HB_MAX_NESTING_LEVEL,
 			 scratch};
 
+    hb_vector_t<float> coords_f32;
+    if (font->num_coords && unlikely (!coords_f32.resize (font->num_coords)))
+      return false;
+    for (unsigned i = 0; i < font->num_coords; i++)
+      coords_f32[i] = (float) font->coords[i];
+
     return get_path_at (c, gid,
-			hb_array (font->coords, font->num_coords));
+			coords_f32.as_array ());
   }
 
   bool
@@ -140,8 +149,14 @@
 			 HB_MAX_NESTING_LEVEL,
 			 scratch};
 
+    hb_vector_t<float> coords_f32;
+    if (font->num_coords && unlikely (!coords_f32.resize (font->num_coords)))
+      return false;
+    for (unsigned i = 0; i < font->num_coords; i++)
+      coords_f32[i] = (float) font->coords[i];
+
     return get_path_at (c, gid,
-			hb_array (font->coords, font->num_coords));
+			coords_f32.as_array ());
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
diff --git a/src/hb-open-type.hh b/src/hb-open-type.hh
index e7c62cf..71982ce 100644
--- a/src/hb-open-type.hh
+++ b/src/hb-open-type.hh
@@ -1963,6 +1963,14 @@
     }
 
     private:
+    HB_ALWAYS_INLINE
+    static float mul_unfused (float a, float b)
+    {
+      // Keep mul/add unfused for cross-engine numeric debugging.
+      volatile float product = a * b;
+      return product;
+    }
+
     template <bool scaled>
     void _add_to (hb_array_t<float> out, float scale = 1.0f)
     {
@@ -1988,14 +1996,14 @@
 #ifndef HB_OPTIMIZE_SIZE
 	    for (; j + 3 < count; j += 4)
 	    {
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
 	    }
 #endif
 	    for (; j < count; j++)
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
 
 	    p = (const unsigned char *) pp;
 	  }
@@ -2007,14 +2015,14 @@
 #ifndef HB_OPTIMIZE_SIZE
 	    for (; j + 3 < count; j += 4)
 	    {
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
 	    }
 #endif
 	    for (; j < count; j++)
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
 
 	    p = (const unsigned char *) pp;
 	  }
@@ -2023,7 +2031,7 @@
 	  {
 	    const auto *pp = (const HBINT32 *) p;
 	    for (unsigned j = 0; j < count; j++)
-	      *arrayZ++ += scaled ? *pp++ * scale : *pp++;
+	      *arrayZ++ += scaled ? mul_unfused (*pp++, scale) : *pp++;
 
 	    p = (const unsigned char *) pp;
 	  }
diff --git a/src/hb-ot-layout-common.hh b/src/hb-ot-layout-common.hh
index 0547301..d22addb 100644
--- a/src/hb-ot-layout-common.hh
+++ b/src/hb-ot-layout-common.hh
@@ -2467,30 +2467,36 @@
 
 struct VarRegionAxis
 {
-  float evaluate (int coord) const
+  float evaluate (float coord) const
   {
-    int peak = peakCoord.to_int ();
-    if (peak == 0 || coord == peak)
+    float peak = (float) peakCoord.to_int ();
+    if (peak == 0.f || coord == peak)
       return 1.f;
-    else if (coord == 0) // Faster
+    else if (coord == 0.f) // Faster
       return 0.f;
 
-    int start = startCoord.to_int (), end = endCoord.to_int ();
+    float start = (float) startCoord.to_int ();
+    float end = (float) endCoord.to_int ();
 
     /* TODO Move these to sanitize(). */
     if (unlikely (start > peak || peak > end))
       return 1.f;
-    if (unlikely (start < 0 && end > 0 && peak != 0))
+    if (unlikely (start < 0.f && end > 0.f && peak != 0.f))
       return 1.f;
 
     if (coord <= start || end <= coord)
       return 0.f;
 
-    /* Interpolate */
+    /* Interpolate in raw 2.14 space. */
     if (coord < peak)
-      return float (coord - start) / (peak - start);
+      return (coord - start) / (peak - start);
     else
-      return float (end - coord) / (end - peak);
+      return (end - coord) / (end - peak);
+  }
+
+  float evaluate (int coord) const
+  {
+    return evaluate ((float) coord);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -2521,6 +2527,13 @@
     return axis.evaluate (coord);
   }
 
+  float evaluate (const float *coords, unsigned int coord_len) const
+  {
+    unsigned i = axisIndex;
+    float coord = i < coord_len ? coords[i] : 0.f;
+    return axis.evaluate (coord);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -2813,7 +2826,21 @@
     {
       float factor = arrayZ[i].evaluate (coords, coord_len);
       if (factor == 0.f)
-	return 0.;
+        return 0.f;
+      v *= factor;
+    }
+    return v;
+  }
+
+  float evaluate (const float *coords, unsigned int coord_len) const
+  {
+    float v = 1.f;
+    unsigned int count = len;
+    for (unsigned int i = 0; i < count; i++)
+    {
+      float factor = arrayZ[i].evaluate (coords, coord_len);
+      if (factor == 0.f)
+        return 0.f;
       v *= factor;
     }
     return v;
@@ -2824,8 +2851,8 @@
 {
   HB_ALWAYS_INLINE
   float evaluate (unsigned int region_index,
-		  const int *coords, unsigned int coord_len,
-		  hb_scalar_cache_t *cache = nullptr) const
+                  const int *coords, unsigned int coord_len,
+                  hb_scalar_cache_t *cache = nullptr) const
   {
     if (unlikely (region_index >= regions.len))
       return 0.;
@@ -2843,6 +2870,34 @@
     return v;
   }
 
+  HB_ALWAYS_INLINE
+  float evaluate (unsigned int region_index,
+                  const float *coords, unsigned int coord_len,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    if (unlikely (region_index >= regions.len))
+      return 0.;
+
+    float v;
+    if (cache && cache->get (region_index, &v))
+      return v;
+
+    const SparseVariationRegion &region = this+regions[region_index];
+
+    v = region.evaluate (coords, coord_len);
+    if (cache)
+      cache->set (region_index, v);
+
+    return v;
+  }
+
+  float evaluate (unsigned int region_index,
+                  hb_array_t<const float> coords,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    return evaluate (region_index, coords.arrayZ, coords.length, cache);
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -3247,37 +3302,116 @@
 	 + StructAfter<CFF2Index> (regionIndices).get_size ();
   }
 
-  void get_delta (unsigned int inner,
-		  const int *coords, unsigned int coord_count,
-		  const SparseVarRegionList &regions,
-		  hb_array_t<float> out,
-		  hb_scalar_cache_t *cache = nullptr) const
+  template <typename CoordT>
+  void get_delta_impl (unsigned int inner,
+                       const CoordT *coords, unsigned int coord_count,
+                       const SparseVarRegionList &regions,
+                       hb_array_t<float> out,
+                       hb_scalar_cache_t *cache = nullptr) const
   {
     auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
 
+    if (unlikely (!out.length))
+      return;
+
     auto values_iter = deltaSets.fetcher (inner);
     unsigned regionCount = regionIndices.len;
     unsigned skip = 0;
     for (unsigned regionIndex = 0; regionIndex < regionCount; regionIndex++)
     {
       float scalar = regions.evaluate (regionIndices.arrayZ[regionIndex],
-				       coords, coord_count,
-				       cache);
+                                       coords, coord_count,
+                                       cache);
       // We skip lazily. Helps with the tail end.
       if (scalar == 0.0f)
         skip += out.length;
       else
       {
         if (skip)
-	{
-	  values_iter.skip (skip);
-	  skip = 0;
-	}
-	values_iter.add_to (out, scalar);
+        {
+          values_iter.skip (skip);
+          skip = 0;
+        }
+        values_iter.add_to (out, scalar);
       }
     }
   }
 
+  void get_delta (unsigned int inner,
+                  const int *coords, unsigned int coord_count,
+                  const SparseVarRegionList &regions,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    get_delta_impl (inner, coords, coord_count, regions, out, cache);
+  }
+
+  void get_delta (unsigned int inner,
+                  const float *coords, unsigned int coord_count,
+                  const SparseVarRegionList &regions,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    get_delta_impl (inner, coords, coord_count, regions, out, cache);
+  }
+
+  void debug_rotation_region_terms (unsigned int inner,
+                                    const float *coords, unsigned int coord_count,
+                                    const SparseVarRegionList &regions,
+                                    unsigned int tuple_len,
+                                    unsigned int rotation_index,
+                                    unsigned int parent_gid,
+                                    unsigned int gid,
+                                    unsigned int var_idx,
+                                    hb_scalar_cache_t *cache = nullptr) const
+  {
+    if (unlikely (!tuple_len || rotation_index >= tuple_len || tuple_len > 9))
+      return;
+
+    auto &deltaSets = StructAfter<decltype (deltaSetsX)> (regionIndices);
+    auto values_iter = deltaSets.fetcher (inner);
+    unsigned regionCount = regionIndices.len;
+    unsigned skip = 0;
+    float tmp[9] = {};
+    float running_f32 = 0.0f;
+    double running_f64 = 0.0;
+
+    for (unsigned region_order = 0; region_order < regionCount; region_order++)
+    {
+      unsigned region_index = regionIndices.arrayZ[region_order];
+      float scalar = regions.evaluate (region_index, coords, coord_count, cache);
+      if (scalar == 0.0f)
+      {
+        skip += tuple_len;
+        fprintf (stderr,
+                 "VARC_ROT_REGION parent_gid=%u gid=%u var_idx=%u region_order=%u region_idx=%u scalar=%.16f raw=%.16f contrib=%.16f running_f32=%.16f running_f64=%.16f\n",
+                 parent_gid, gid, var_idx, region_order, region_index,
+                 0.0, 0.0, 0.0, (double) running_f32, running_f64);
+        continue;
+      }
+
+      if (skip)
+      {
+        values_iter.skip (skip);
+        skip = 0;
+      }
+
+      for (unsigned i = 0; i < tuple_len; i++)
+        tmp[i] = 0.0f;
+      values_iter.add_to (hb_array (tmp, tuple_len), 1.0f);
+
+      float raw = tmp[rotation_index];
+      float contrib = raw * scalar;
+      running_f32 += contrib;
+      running_f64 += (double) raw * (double) scalar;
+      fprintf (stderr,
+               "VARC_ROT_REGION parent_gid=%u gid=%u var_idx=%u region_order=%u region_idx=%u scalar=%.16f raw=%.16f contrib=%.16f running_f32=%.16f running_f64=%.16f\n",
+               parent_gid, gid, var_idx, region_order, region_index,
+               (double) scalar, (double) raw, (double) contrib,
+               (double) running_f32, running_f64);
+    }
+  }
+
   bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
@@ -3569,44 +3703,118 @@
   }
 
   private:
-  void get_delta (unsigned int outer, unsigned int inner,
-		  const int *coords, unsigned int coord_count,
-		  hb_array_t<float> out,
-		  hb_scalar_cache_t *cache = nullptr) const
+  template <typename CoordT>
+  void get_delta_impl (unsigned int outer, unsigned int inner,
+                       const CoordT *coords, unsigned int coord_count,
+                       hb_array_t<float> out,
+                       hb_scalar_cache_t *cache = nullptr) const
   {
 #ifdef HB_NO_VAR
     return;
 #endif
 
+    if (unlikely (!out.length))
+      return;
+
+    if (unlikely (!coords && coord_count))
+      return;
+
+    if (unlikely (!coords))
+      coord_count = 0;
+
     if (unlikely (outer >= dataSets.len))
       return;
 
     return (this+dataSets[outer]).get_delta (inner,
-					     coords, coord_count,
-					     this+regions,
-					     out,
-					     cache);
+                                             coords, coord_count,
+                                             this+regions,
+                                             out,
+                                             cache);
+  }
+
+  void get_delta (unsigned int outer, unsigned int inner,
+                  const int *coords, unsigned int coord_count,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    get_delta_impl (outer, inner, coords, coord_count, out, cache);
+  }
+
+  void get_delta (unsigned int outer, unsigned int inner,
+                  const float *coords, unsigned int coord_count,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    get_delta_impl (outer, inner, coords, coord_count, out, cache);
   }
 
   public:
   void get_delta (unsigned int index,
-		  const int *coords, unsigned int coord_count,
-		  hb_array_t<float> out,
-		  hb_scalar_cache_t *cache = nullptr) const
+                  const int *coords, unsigned int coord_count,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
   {
     unsigned int outer = index >> 16;
     unsigned int inner = index & 0xFFFF;
     get_delta (outer, inner, coords, coord_count, out, cache);
   }
+
   void get_delta (unsigned int index,
-		  hb_array_t<const int> coords,
-		  hb_array_t<float> out,
-		  hb_scalar_cache_t *cache = nullptr) const
+                  const float *coords, unsigned int coord_count,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    unsigned int outer = index >> 16;
+    unsigned int inner = index & 0xFFFF;
+    get_delta (outer, inner, coords, coord_count, out, cache);
+  }
+
+  void get_delta (unsigned int index,
+                  hb_array_t<const int> coords,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
   {
     return get_delta (index,
-		      coords.arrayZ, coords.length,
-		      out,
-		      cache);
+                      coords.arrayZ, coords.length,
+                      out,
+                      cache);
+  }
+
+  void get_delta (unsigned int index,
+                  hb_array_t<const float> coords,
+                  hb_array_t<float> out,
+                  hb_scalar_cache_t *cache = nullptr) const
+  {
+    return get_delta (index,
+                      coords.arrayZ, coords.length,
+                      out,
+                      cache);
+  }
+
+  void debug_rotation_region_terms (unsigned int index,
+                                    const float *coords, unsigned int coord_count,
+                                    unsigned int tuple_len,
+                                    unsigned int rotation_index,
+                                    unsigned int parent_gid,
+                                    unsigned int gid,
+                                    hb_scalar_cache_t *cache = nullptr) const
+  {
+#ifdef HB_NO_VAR
+    return;
+#endif
+    unsigned int outer = index >> 16;
+    unsigned int inner = index & 0xFFFF;
+    if (unlikely (!coords && coord_count))
+      return;
+    if (unlikely (!coords))
+      coord_count = 0;
+    if (unlikely (outer >= dataSets.len))
+      return;
+    return (this+dataSets[outer]).debug_rotation_region_terms (inner,
+                                                                coords, coord_count,
+                                                                this+regions,
+                                                                tuple_len, rotation_index,
+                                                                parent_gid, gid, index, cache);
   }
 
   bool sanitize (hb_sanitize_context_t *c) const
@@ -3863,16 +4071,21 @@
 struct MultiItemVarStoreInstancer
 {
   MultiItemVarStoreInstancer (const MultiItemVariationStore *varStore,
-			      const DeltaSetIndexMap *varIdxMap,
-			      hb_array_t<const int> coords,
-			      hb_scalar_cache_t *cache = nullptr) :
-    varStore (varStore), varIdxMap (varIdxMap), coords (coords), cache (cache)
+                              const DeltaSetIndexMap *varIdxMap,
+                              hb_array_t<const int> coords,
+                              hb_array_t<const float> coords_f32 = hb_array_t<const float> (),
+                              hb_scalar_cache_t *cache = nullptr) :
+    varStore (varStore),
+    varIdxMap (varIdxMap),
+    coords (coords),
+    coords_f32 (coords_f32),
+    cache (cache)
   {
     if (!varStore)
       varStore = &Null(MultiItemVariationStore);
   }
 
-  operator bool () const { return varStore && bool (coords); }
+  operator bool () const { return varStore && (bool (coords_f32) || bool (coords)); }
 
   float operator[] (uint32_t varIdx) const
   {
@@ -3883,12 +4096,15 @@
 
   void operator() (hb_array_t<float> out, uint32_t varIdx, unsigned short offset = 0) const
   {
-    if (coords && varIdx != VarIdx::NO_VARIATION)
+    if ((coords_f32 || coords) && varIdx != VarIdx::NO_VARIATION)
     {
       varIdx += offset;
       if (varIdxMap)
-	varIdx = varIdxMap->map (varIdx);
-      varStore->get_delta (varIdx, coords, out, cache);
+        varIdx = varIdxMap->map (varIdx);
+      if (coords_f32)
+        varStore->get_delta (varIdx, coords_f32, out, cache);
+      else
+        varStore->get_delta (varIdx, coords, out, cache);
     }
     else
       for (unsigned i = 0; i < out.length; i++)
@@ -3898,6 +4114,7 @@
   const MultiItemVariationStore *varStore;
   const DeltaSetIndexMap *varIdxMap;
   hb_array_t<const int> coords;
+  hb_array_t<const float> coords_f32;
   hb_scalar_cache_t *cache;
 };