WIP: harfbuzz renderfont
diff --git a/skia/viewer/src/drawtext.cpp b/skia/viewer/src/drawtext.cpp
index 6a5af71..6b77f7f 100644
--- a/skia/viewer/src/drawtext.cpp
+++ b/skia/viewer/src/drawtext.cpp
@@ -6,6 +6,8 @@
 #include "skia_renderer.hpp"
 #include "skia_rive_fontmgr.hpp"
 
+extern rive::rcp<rive::RenderFont> rive_make_harfbuzz_renderfont(rive::Span<const uint8_t>);
+
 #include <string>
 
 static void drawrun(rive::Factory* factory, rive::Renderer* renderer,
diff --git a/skia/viewer/src/hbfont.cpp b/skia/viewer/src/hbfont.cpp
new file mode 100644
index 0000000..a740fba
--- /dev/null
+++ b/skia/viewer/src/hbfont.cpp
@@ -0,0 +1,205 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#include "rive/factory.hpp"
+#include "rive/render_text.hpp"
+
+#include <harfbuzz/hb.h>
+#include <harfbuzz/hb-ot.h>
+
+class HBRenderFont : public rive::RenderFont {
+    hb_font_t* m_Font;
+    hb_draw_funcs_t* m_DrawFuncs;
+
+public:
+
+    // We assume ownership of font!
+    HBRenderFont(hb_font_t* font);
+    ~HBRenderFont() override;
+
+    std::vector<Axis> getAxes() const override;
+    std::vector<Coord> getCoords() const override;
+    rive::rcp<rive::RenderFont> makeAtCoords(rive::Span<const Coord>) const override;
+    rive::RawPath getPath(rive::GlyphID) const override;
+
+    static rive::rcp<rive::RenderFont> DecodeFont(rive::Span<const uint8_t>);
+    std::vector<rive::RenderGlyphRun> ShapeText(rive::Span<const rive::Unichar> text,
+                                                rive::Span<const rive::RenderTextRun> runs);
+};
+
+rive::rcp<rive::RenderFont> rive_make_harfbuzz_renderfont(rive::Span<const uint8_t> span) {
+    return HBRenderFont::DecodeFont(span);
+}
+
+//////////////
+
+constexpr int kStdScale = 2048;
+constexpr float gInvScale = 64.0f / kStdScale;
+
+extern "C" {
+void rpath_move_to(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, float x, float y, void*) {
+    ((rive::RawPath*)rpath)->moveTo(x, y);
+}
+void rpath_line_to(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, float x1, float y1, void*) {
+    ((rive::RawPath*)rpath)->lineTo(x1, y1);
+}
+void rpath_quad_to(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, float x1, float y1, float x2, float y2, void*) {
+    ((rive::RawPath*)rpath)->quadTo(x1, y1, x2, y2);
+}
+void rpath_cubic_to(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*,
+                    float x1, float y1, float x2, float y2, float x3, float y3, void*) {
+    ((rive::RawPath*)rpath)->cubicTo(x1, y1, x2, y2, x3, y3);
+}
+void rpath_close(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, void*) {
+    ((rive::RawPath*)rpath)->close();
+}
+}
+
+HBRenderFont::HBRenderFont(hb_font_t* font) : m_Font(font) {
+    // we assume ownership -- no need to call reference()
+    hb_font_set_scale(m_Font, kStdScale, kStdScale);
+
+    m_DrawFuncs = hb_draw_funcs_create();
+    hb_draw_funcs_set_move_to_func(m_DrawFuncs, rpath_move_to, nullptr, nullptr);
+    hb_draw_funcs_set_line_to_func(m_DrawFuncs, rpath_line_to, nullptr, nullptr);
+    hb_draw_funcs_set_quadratic_to_func(m_DrawFuncs, rpath_quad_to, nullptr, nullptr);
+    hb_draw_funcs_set_cubic_to_func(m_DrawFuncs, rpath_cubic_to, nullptr, nullptr);
+    hb_draw_funcs_set_close_path_func(m_DrawFuncs, rpath_close, nullptr, nullptr);
+    hb_draw_funcs_make_immutable(m_DrawFuncs);
+}
+
+HBRenderFont::~HBRenderFont() {
+    hb_draw_funcs_destroy(m_DrawFuncs);
+    hb_font_destroy(m_Font);
+}
+
+rive::rcp<rive::RenderFont> HBRenderFont::DecodeFont(rive::Span<const uint8_t> span) {
+    auto blob = hb_blob_create_or_fail((const char*)span.data(), (unsigned)span.size(),
+                                             HB_MEMORY_MODE_DUPLICATE, nullptr, nullptr);
+    if (blob) {
+       auto face = hb_face_create(blob, 0);
+        hb_blob_destroy(blob);
+        if (face) {
+            auto font = hb_font_create(face);
+            hb_face_destroy(face);
+            if (font) {
+                return rive::rcp<rive::RenderFont>(new HBRenderFont(font));
+            }
+        }
+    }
+    return nullptr;
+}
+
+
+std::vector<rive::RenderFont::Axis> HBRenderFont::getAxes() const {
+    auto face = hb_font_get_face(m_Font);
+    std::vector<rive::RenderFont::Axis> axes;
+
+    const int count = hb_ot_var_get_axis_count(face);
+    if (count > 0) {
+        axes.resize(count);
+
+        hb_ot_var_axis_info_t info;
+        for (int i = 0; i < count; ++i) {
+            unsigned n = 1;
+            hb_ot_var_get_axis_infos(face, i, &n, &info);
+            assert(n == 1);
+            axes[i] = { info.tag, info.min_value, info.default_value, info.max_value };
+        }
+    }
+    return axes;
+}
+
+std::vector<rive::RenderFont::Coord> HBRenderFont::getCoords() const {
+    auto axes = this->getAxes();
+    const int count = (int)axes.size();
+
+    unsigned length;
+    const float* values = hb_font_get_var_coords_design(m_Font, &length);
+    assert((unsigned)count == length);
+
+    std::vector<rive::RenderFont::Coord> coords(count);
+    for (int i = 0; i < count; ++i) {
+        assert(values[i] >= axes[i].min && values[i] >= axes[i].max);
+        coords[i] = { axes[i].tag, values[i] };
+    }
+    return coords;
+}
+
+rive::rcp<rive::RenderFont> HBRenderFont::makeAtCoords(rive::Span<const Coord> coords) const {
+    const int count = (int)coords.size();
+    std::vector<hb_variation_t> vars(count);
+    for (int i = 0; i < count; ++i) {
+        vars[i].tag = coords[i].axis;
+        vars[i].value = coords[i].value;
+    }
+
+    auto font = hb_font_create_sub_font(m_Font);
+    hb_font_set_variations(font, vars.data(), count);
+    return rive::rcp<rive::RenderFont>(new HBRenderFont(font));
+}
+
+rive::RawPath HBRenderFont::getPath(rive::GlyphID glyph) const {
+    rive::RawPath rpath;
+    hb_font_get_glyph_shape(m_Font, glyph, m_DrawFuncs, &rpath);
+    return rpath;
+}
+
+///////////////////////////////////////////////////////////
+#if 0
+static float shapeRun(rive::RenderGlyphRun* grun, const rive::RenderTextRun& trun,
+                      const rive::Unichar text[], size_t textOffset, float origin) {
+    grun->font = trun.font;
+    grun->size = trun.size;
+    grun->startTextIndex = textOffset;
+    grun->glyphs.resize(trun.unicharCount);
+    grun->xpos.resize(trun.unicharCount + 1);
+
+    const int count = SkToInt(trun.unicharCount);
+
+    SkiaRenderFont* rfont = static_cast<SkiaRenderFont*>(trun.font.get());
+    rfont->m_Typeface->unicharsToGlyphs(text + textOffset, count, grun->glyphs.data());
+
+    SkFont font(rfont->m_Typeface, grun->size);
+    setupFont(&font);
+
+    // We get 'widths' from skia, but then turn them into xpos
+    // this will write count values, but xpos has count+1 slots
+    font.getWidths(grun->glyphs.data(), count, grun->xpos.data());
+    for (auto& xp : grun->xpos) {
+        auto width = xp;
+        xp = origin;
+        origin += width;
+    }
+
+    return grun->xpos.data()[count];
+}
+
+std::vector<rive::RenderGlyphRun>
+SkiaRiveFontMgr::shapeText(rive::Span<const rive::Unichar> text,
+                           rive::Span<const rive::RenderTextRun> truns) {
+    std::vector<rive::RenderGlyphRun> gruns;
+
+    // sanity check
+    size_t count = 0;
+    for (const auto& tr : truns) {
+        count += tr.unicharCount;
+    }
+    if (count > text.size()) {
+        return gruns;   // not enough text, so abort
+    }
+
+    gruns.resize(truns.size());
+    int i = 0;
+    size_t offset = 0;
+    float origin = 0;
+    for (const auto& tr : truns) {
+        origin = shapeRun(&gruns[i], tr, text.data(), offset, origin);
+        offset += tr.unicharCount;
+        i += 1;
+    }
+
+    return gruns;
+}
+#endif