[aat] Implement trak logic (#816)

diff --git a/src/hb-aat-layout-kerx-table.hh b/src/hb-aat-layout-kerx-table.hh
index b061f11..06282e2 100644
--- a/src/hb-aat-layout-kerx-table.hh
+++ b/src/hb-aat-layout-kerx-table.hh
@@ -263,6 +263,13 @@
 {
   static const hb_tag_t tableTag = HB_AAT_TAG_kerx;
 
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    /* TODO */
+    return_trace (false);
+  }
+
   struct SubTableWrapper
   {
     enum coverage_flags_t {
diff --git a/src/hb-aat-layout-private.hh b/src/hb-aat-layout-private.hh
index c1c607a..ce75c8e 100644
--- a/src/hb-aat-layout-private.hh
+++ b/src/hb-aat-layout-private.hh
@@ -37,4 +37,7 @@
 HB_INTERNAL void
 hb_aat_layout_substitute (hb_font_t *font, hb_buffer_t *buffer);
 
+HB_INTERNAL void
+hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer);
+
 #endif /* HB_AAT_LAYOUT_PRIVATE_HH */
diff --git a/src/hb-aat-layout-trak-table.hh b/src/hb-aat-layout-trak-table.hh
index 6dbd05a..a5dc3ff 100644
--- a/src/hb-aat-layout-trak-table.hh
+++ b/src/hb-aat-layout-trak-table.hh
@@ -1,6 +1,6 @@
 /*
- * Copyright © 2018  Google, Inc.
  * Copyright © 2018  Ebrahim Byagowi
+ * Copyright © 2018  Google, Inc.
  *
  *  This is part of HarfBuzz, a text shaping library.
  *
@@ -45,10 +45,15 @@
     return_trace (c->check_struct (this));
   }
 
+  inline float get_value (const void *base, unsigned int index) const
+  {
+    return (base+values)[index];
+  }
+
   protected:
   Fixed			track;		/* Track value for this record. */
   HBUINT16		trackNameID;	/* The 'name' table index for this track */
-  OffsetTo<UnsizedArrayOf<Fixed> >
+  OffsetTo<UnsizedArrayOf<HBINT16> >
 			values;		/* Offset from start of tracking table to
 					 * per-size tracking values for this track. */
 
@@ -61,15 +66,48 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* TODO */
     return_trace (c->check_struct (this));
   }
 
+  inline float get_tracking (const void *base, float ptem) const
+  {
+    /* CoreText points are CSS pixels (96 per inch),
+     * NOT typographic points (72 per inch).
+     *
+     * https://developer.apple.com/library/content/documentation/GraphicsAnimation/Conceptual/HighResolutionOSX/Explained/Explained.html
+     */
+    float csspx = ptem * 96.f / 72.f;
+    Fixed fixed_size;
+    fixed_size.set_float (csspx);
+
+    // TODO: Make indexing work and use only an entry with zero track
+    const TrackTableEntry trackTableEntry = trackTable[0];
+
+    unsigned int size_index;
+    for (size_index = 0; size_index < nSizes; ++size_index)
+      if ((base+sizeTable)[size_index] >= fixed_size)
+        break;
+
+    // We don't attempt to extrapolate to larger or smaller values
+    if (size_index == nSizes)
+      return trackTableEntry.get_value (base, nSizes - 1);
+    if (size_index == 0 || (base+sizeTable)[size_index] == fixed_size)
+      return trackTableEntry.get_value (base, size_index);
+
+    float s0 = (base+sizeTable)[size_index - 1].to_float ();
+    float s1 = (base+sizeTable)[size_index].to_float ();
+    float t = (csspx - s0) / (s1 - s0);
+    return t * trackTableEntry.get_value (base, size_index) +
+      (1.0 - t) * trackTableEntry.get_value (base, size_index - 1);
+  }
+
   protected:
   HBUINT16		nTracks;	/* Number of separate tracks included in this table. */
   HBUINT16		nSizes;		/* Number of point sizes included in this table. */
   LOffsetTo<UnsizedArrayOf<Fixed> >
 			sizeTable;
-  TrackTableEntry	trackTable[VAR];/* Array[nSizes] of size values. */
+  UnsizedArrayOf<TrackTableEntry>	trackTable;/* Array[nSizes] of size values. */
 
   public:
   DEFINE_SIZE_ARRAY (8, trackTable);
@@ -82,9 +120,43 @@
   inline bool sanitize (hb_sanitize_context_t *c) const
   {
     TRACE_SANITIZE (this);
+    /* TODO */
     return_trace (c->check_struct (this));
   }
 
+  inline bool apply (hb_aat_apply_context_t *c) const
+  {
+    TRACE_APPLY (this);
+    const float ptem = c->font->ptem;
+    if (ptem > 0.f)
+    {
+      hb_buffer_t *buffer = c->buffer;
+      if (HB_DIRECTION_IS_HORIZONTAL (c->buffer->props.direction))
+      {
+        const TrackData trackData = this+horizOffset;
+        float tracking = trackData.get_tracking (this, ptem);
+        hb_position_t advance_to_add = c->font->em_scalef_x (tracking / 2);
+        foreach_grapheme (buffer, start, end)
+        {
+          buffer->pos[start].x_advance += advance_to_add;
+          buffer->pos[end].x_advance += advance_to_add;
+        }
+      }
+      else
+      {
+        const TrackData trackData = this+vertOffset;
+        float tracking = trackData.get_tracking (this, ptem);
+        hb_position_t advance_to_add = c->font->em_scalef_y (tracking / 2);
+        foreach_grapheme (buffer, start, end)
+        {
+          buffer->pos[start].y_advance += advance_to_add;
+          buffer->pos[end].y_advance += advance_to_add;
+        }
+      }
+    }
+    return_trace (false);
+  }
+
   protected:
   FixedVersion<>	version;	/* Version of the tracking table--currently
 					 * 0x00010000u for version 1.0. */
diff --git a/src/hb-aat-layout.cc b/src/hb-aat-layout.cc
index 3b967c6..2b67bf3 100644
--- a/src/hb-aat-layout.cc
+++ b/src/hb-aat-layout.cc
@@ -55,6 +55,40 @@
   return morx;
 }
 
+static inline const AAT::kerx&
+_get_kerx (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return OT::Null(AAT::kerx);
+  }
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  /* XXX this doesn't call set_num_glyphs on sanitizer. */
+  const AAT::kerx& kerx = *(layout->kerx.get ());
+  if (blob)
+    *blob = layout->kerx.blob;
+  return kerx;
+}
+
+static inline const AAT::trak&
+_get_trak (hb_face_t *face, hb_blob_t **blob = nullptr)
+{
+  if (unlikely (!hb_ot_shaper_face_data_ensure (face)))
+  {
+    if (blob)
+      *blob = hb_blob_get_empty ();
+    return OT::Null(AAT::trak);
+  }
+  hb_ot_layout_t * layout = hb_ot_layout_from_face (face);
+  /* XXX this doesn't call set_num_glyphs on sanitizer. */
+  const AAT::trak& trak = *(layout->trak.get ());
+  if (blob)
+    *blob = layout->trak.blob;
+  return trak;
+}
+
 static inline void
 _hb_aat_layout_create (hb_face_t *face)
 {
@@ -78,3 +112,15 @@
   AAT::hb_aat_apply_context_t c (font, buffer, blob);
   morx.apply (&c);
 }
+
+void
+hb_aat_layout_position (hb_font_t *font, hb_buffer_t *buffer)
+{
+  hb_blob_t *blob;
+  const AAT::kerx& kerx = _get_kerx (font->face, &blob);
+  const AAT::trak& trak = _get_trak (font->face, &blob);
+
+  AAT::hb_aat_apply_context_t c (font, buffer, blob);
+  kerx.apply (&c);
+  trak.apply (&c);
+}
diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh
index f5c20bc..e465788 100644
--- a/src/hb-open-type-private.hh
+++ b/src/hb-open-type-private.hh
@@ -692,8 +692,8 @@
 /* 32-bit signed fixed-point number (16.16). */
 struct Fixed: HBINT32
 {
-  //inline float to_float (void) const { return ???; }
-  //inline void set_float (float f) { v.set (f * ???); }
+  inline float to_float (void) const { return ((int32_t) v) / 65536.0; }
+  inline void set_float (float f) { v.set (round (f * 65536.0)); }
   public:
   DEFINE_SIZE_STATIC (4);
 };
diff --git a/src/hb-ot-layout-private.hh b/src/hb-ot-layout-private.hh
index 0c3bcbc..ce1f6a8 100644
--- a/src/hb-ot-layout-private.hh
+++ b/src/hb-ot-layout-private.hh
@@ -363,6 +363,28 @@
   return _hb_glyph_info_is_unicode_mark (info) ? info->unicode_props()>>8 : 0;
 }
 
+
+/* Loop over grapheme. Based on foreach_cluster(). */
+#define foreach_grapheme(buffer, start, end) \
+  for (unsigned int \
+       _count = buffer->len, \
+       start = 0, end = _count ? _next_grapheme (buffer, 0) : 0; \
+       start < _count; \
+       start = end, end = _next_grapheme (buffer, start))
+
+static inline unsigned int
+_next_grapheme (hb_buffer_t *buffer, unsigned int start)
+{
+  hb_glyph_info_t *info = buffer->info;
+  unsigned int count = buffer->len;
+
+  while (++start < count && _hb_glyph_info_is_unicode_mark (&info[start]))
+    ;
+
+  return start;
+}
+
+
 #define info_cc(info) (_hb_glyph_info_get_modified_combining_class (&(info)))
 
 static inline bool
diff --git a/src/hb-ot-layout.cc b/src/hb-ot-layout.cc
index 4cf6c72..b92ba05 100644
--- a/src/hb-ot-layout.cc
+++ b/src/hb-ot-layout.cc
@@ -65,6 +65,8 @@
   layout->fvar.init (face);
   layout->avar.init (face);
   layout->morx.init (face);
+  layout->kerx.init (face);
+  layout->trak.init (face);
 
   {
     /*
@@ -215,6 +217,8 @@
   layout->fvar.fini ();
   layout->avar.fini ();
   layout->morx.fini ();
+  layout->kerx.fini ();
+  layout->trak.fini ();
 
   free (layout);
 }
diff --git a/src/hb-ot-shape.cc b/src/hb-ot-shape.cc
index d9ba0f6..d2d8012 100644
--- a/src/hb-ot-shape.cc
+++ b/src/hb-ot-shape.cc
@@ -787,6 +787,8 @@
     _hb_ot_shape_fallback_kern (c->plan, c->font, c->buffer);
 
   _hb_buffer_deallocate_gsubgpos_vars (c->buffer);
+
+  //hb_aat_layout_position (c->font, c->buffer);
 }
 
 static inline void