rebase
diff --git a/include/rive/render_text.hpp b/include/rive/render_text.hpp
new file mode 100644
index 0000000..20d7206
--- /dev/null
+++ b/include/rive/render_text.hpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifndef _RIVE_RENDER_TEXT_HPP_
+#define _RIVE_RENDER_TEXT_HPP_
+
+#include "rive/math/raw_path.hpp"
+#include "rive/refcnt.hpp"
+#include "rive/span.hpp"
+
+namespace rive {
+
+using Unichar = int32_t;
+using GlyphID = uint16_t;
+
+class RenderFont : public RefCnt {
+public:
+ // This is experimental
+ // -- may only be needed by Editor
+ // -- so it may be removed from here later
+ //
+ struct Axis {
+ uint32_t tag;
+ float min;
+ float def; // default value
+ float max;
+ };
+
+ // Returns the canonical set of Axes for this font. Use this to know
+ // what variations are possible. If you want to know the specific
+ // coordinate within that variations space for *this* font, call
+ // getCoords().
+ //
+ virtual std::vector<Axis> getAxes() const = 0;
+
+ struct Coord {
+ uint32_t axis;
+ float value;
+ };
+
+ // Returns the specific coords in variation space for this font.
+ // If you want to have a description of the entire variation space,
+ // call getAxes().
+ //
+ virtual std::vector<Coord> getCoords() const = 0;
+
+ virtual rcp<RenderFont> makeAtCoords(Span<const Coord>) const = 0;
+
+ rcp<RenderFont> makeAtCoord(Coord c) {
+ return this->makeAtCoords(Span<const Coord>(&c, 1));
+ }
+
+ // Returns a 1-point path for this glyph. It will be positioned
+ // relative to (0,0) with the typographic baseline at y = 0.
+ //
+ virtual RawPath getPath(GlyphID) const = 0;
+};
+
+struct RenderTextRun {
+ rcp<RenderFont> font;
+ float size;
+ uint32_t unicharCount;
+};
+
+struct RenderGlyphRun {
+ rcp<RenderFont> font;
+ float size;
+ uint32_t startTextIndex;
+
+ std::vector<GlyphID> glyphs;
+ std::vector<float> xpos; // xpos.size() == glyphs.size() + 1
+};
+
+} // namespace rive
+#endif
diff --git a/include/rive/renderer.hpp b/include/rive/renderer.hpp
index 440d371..85559b9 100644
--- a/include/rive/renderer.hpp
+++ b/include/rive/renderer.hpp
@@ -37,39 +37,6 @@
size_t count() const { return m_Count; }
};
- class RenderFont {
- public:
- struct AxisInfo {
- uint32_t tag;
- float min;
- float def; // default value
- float max;
- };
-
- virtual int countAxes() const { return 0; }
- virtual std::vector<AxisInfo> getAxes() const { return std::vector<AxisInfo>; }
-
- // TODO: getGlyphPath(index) -> rawpath
- };
-
- struct RenderTextRun {
- rcp<RenderFont> font;
- float size;
- uint32_t textCount; // number of unichars in this run in text[]
- };
-
- struct RenderGlyphRun {
- rcp<RenderFont> font;
- float size;
-
- size_t startTextIndex;
- std::vector<uint16_t> glyphs;
- std::vector<float> xpos; // xpos.size() == glyphs.size() + 1
- };
-
- extern std::vector<RenderGlyphRun> shapeText(const uint32_t text[], size_t textCount,
- const RenderTextRun[], size_t runCount);
-
enum class RenderPaintStyle { stroke, fill };
enum class RenderTileMode {
diff --git a/skia/viewer/src/drawtext.cpp b/skia/viewer/src/drawtext.cpp
new file mode 100644
index 0000000..6a5af71
--- /dev/null
+++ b/skia/viewer/src/drawtext.cpp
@@ -0,0 +1,89 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#include "skia_factory.hpp"
+#include "skia_renderer.hpp"
+#include "skia_rive_fontmgr.hpp"
+
+#include <string>
+
+static void drawrun(rive::Factory* factory, rive::Renderer* renderer,
+ const rive::RenderGlyphRun& run, rive::Vec2D origin) {
+ auto font = run.font.get();
+ const auto scale = rive::Mat2D::fromScale(run.size, run.size);
+ auto paint = factory->makeRenderPaint();
+ paint->color(0xFFFFFFFF);
+
+ for (size_t i = 0; i < run.glyphs.size(); ++i) {
+ auto trans = rive::Mat2D::fromTranslate(origin.x + run.xpos[i], origin.y);
+ auto rawpath = font->getPath(run.glyphs[i]);
+ rawpath.transformInPlace(trans * scale);
+ auto path = factory->makeRenderPath(rawpath.points(), rawpath.verbsU8(), rive::FillRule::nonZero);
+ renderer->drawPath(path.get(), paint.get());
+ }
+}
+
+struct UniString {
+ std::vector<rive::Unichar> array;
+
+ UniString(const char text[]) {
+ while (*text) {
+ array.push_back(*text++);
+ }
+ }
+};
+
+static std::string tagstr(uint32_t tag) {
+ std::string s("abcd");
+ s[0] = (tag >> 24) & 0xFF;
+ s[1] = (tag >> 16) & 0xFF;
+ s[2] = (tag >> 8) & 0xFF;
+ s[3] = (tag >> 0) & 0xFF;
+ return s;
+}
+
+void drawtext(rive::Factory* factory, rive::Renderer* renderer) {
+ static std::vector<rive::RenderGlyphRun> gruns;
+
+ if (gruns.size() == 0) {
+ SkiaRiveFontMgr fmgr;
+
+ auto font0 = fmgr.findFont("Times New Roman");
+ auto font1 = fmgr.findFont("Skia");
+
+ if (false) {
+ auto axes = font1->getAxes();
+ for (auto a : axes) {
+ printf("%s %g %g %g\n", tagstr(a.tag).c_str(), a.min, a.def, a.max);
+ }
+ auto coords = font1->getCoords();
+ for (auto c : coords) {
+ printf("%s %g\n", tagstr(c.axis).c_str(), c.value);
+ }
+ }
+
+ rive::RenderFont::Coord c0 = {'wght', 0.5f},
+ c1 = {'wght', 2.0f};
+
+ UniString str("Uneasy lies the head that wears a crown.");
+ rive::RenderTextRun truns[] = {
+ { font0, 60, 1 },
+ { font0, 30, 6 },
+ { font1->makeAtCoord(c0), 30, 4 },
+ { font1, 30, 4 },
+ { font1->makeAtCoord(c1), 30, 5 },
+ { font0, 30, 20 },
+ };
+
+ gruns = fmgr.shapeText(rive::toSpan(str.array), rive::Span(truns, 6));
+ }
+
+ renderer->save();
+ renderer->scale(3, 3);
+ for (const auto& g : gruns) {
+ drawrun(factory, renderer, g, {10, 50});
+ }
+ renderer->restore();
+}
+
diff --git a/skia/viewer/src/fontmgr.cpp b/skia/viewer/src/fontmgr.cpp
new file mode 100644
index 0000000..4298c3c
--- /dev/null
+++ b/skia/viewer/src/fontmgr.cpp
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#include "rive/factory.hpp"
+#include "rive/render_text.hpp"
+#include "skia_rive_fontmgr.hpp"
+
+#include "include/core/SkTypeface.h"
+#include "include/core/SkFont.h"
+#include "include/core/SkPath.h"
+
+class SkiaRenderFont : public rive::RenderFont {
+public:
+ sk_sp<SkTypeface> m_Typeface;
+
+ SkiaRenderFont(sk_sp<SkTypeface> tf) : m_Typeface(std::move(tf)) {}
+
+ 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;
+};
+
+std::vector<rive::RenderFont::Axis> SkiaRenderFont::getAxes() const {
+ std::vector<rive::RenderFont::Axis> axes;
+ const int count = m_Typeface->getVariationDesignParameters(nullptr, 0);
+ if (count > 0) {
+ std::vector<SkFontParameters::Variation::Axis> src(count);
+ (void)m_Typeface->getVariationDesignParameters(src.data(), count);
+ axes.resize(count);
+ for (int i = 0; i < count; ++i) {
+ axes[i] = { src[i].tag, src[i].min, src[i].def, src[i].max };
+ }
+ }
+ return axes;
+}
+
+std::vector<rive::RenderFont::Coord> SkiaRenderFont::getCoords() const {
+ int count = m_Typeface->getVariationDesignPosition(nullptr, 0);
+ std::vector<SkFontArguments::VariationPosition::Coordinate> skcoord(count);
+ m_Typeface->getVariationDesignPosition(skcoord.data(), count);
+
+ std::vector<rive::RenderFont::Coord> coords(count);
+ for (int i = 0; i < count; ++i) {
+ coords[i] = { skcoord[i].axis, skcoord[i].value };
+ }
+ return coords;
+}
+
+rive::rcp<rive::RenderFont> SkiaRenderFont::makeAtCoords(rive::Span<const Coord> coords) const {
+ const int count = (int)coords.size();
+ SkAutoSTArray<16, SkFontArguments::VariationPosition::Coordinate> storage(count);
+ for (size_t i = 0; i < count; ++i) {
+ storage[i].axis = coords[i].axis;
+ storage[i].value = coords[i].value;
+ }
+ SkFontArguments args;
+ args.setVariationDesignPosition({storage.get(), count});
+
+ auto face = m_Typeface->makeClone(args);
+
+ if (face->uniqueID() == m_Typeface->uniqueID()) {
+ auto self = const_cast<SkiaRenderFont*>(this);
+ return rive::rcp<rive::RenderFont>(rive::safe_ref(self));
+ } else {
+ return rive::rcp<rive::RenderFont>(new SkiaRenderFont(std::move(face)));
+ }
+}
+
+static inline rive::Vec2D rv(SkPoint p) { return rive::Vec2D(p.fX, p.fY); }
+
+static void setupFont(SkFont* font) {
+ font->setLinearMetrics(true);
+ font->setBaselineSnap(false);
+ font->setHinting(SkFontHinting::kNone);
+}
+
+rive::RawPath SkiaRenderFont::getPath(rive::GlyphID glyph) const {
+ SkFont font(m_Typeface, 1.0f);
+ setupFont(&font);
+
+ SkPath skpath;
+ font.getPath(glyph, &skpath);
+
+ rive::RawPath rpath;
+ SkPath::RawIter iter(skpath);
+ SkPoint pts[4];
+ bool done = false;
+ while (!done) {
+ switch (iter.next(pts)) {
+ case SkPath::kMove_Verb: rpath.move (rv(pts[0])); break;
+ case SkPath::kLine_Verb: rpath.line (rv(pts[1])); break;
+ case SkPath::kQuad_Verb: rpath.quad (rv(pts[1]), rv(pts[2])); break;
+ case SkPath::kConic_Verb: rpath.quad (rv(pts[1]), rv(pts[2])); break; // TODO: convert
+ case SkPath::kCubic_Verb: rpath.cubic(rv(pts[1]), rv(pts[2]), rv(pts[3])); break;
+ case SkPath::kClose_Verb: rpath.close(); break;
+ case SkPath::kDone_Verb: done = true; break;
+ }
+ }
+ return rpath;
+}
+
+///////////////////////////////////////////////////////////
+
+rive::rcp<rive::RenderFont> SkiaRiveFontMgr::decodeFont(rive::Span<const uint8_t> data) {
+ auto tf = SkTypeface::MakeDefault(); // ignoring data for now
+ return rive::rcp<rive::RenderFont>(new SkiaRenderFont(std::move(tf)));
+}
+
+rive::rcp<rive::RenderFont> SkiaRiveFontMgr::findFont(const char name[]) {
+ auto tf = SkTypeface::MakeFromName(name, SkFontStyle());
+ if (!tf) {
+ tf = SkTypeface::MakeDefault();
+ }
+ return rive::rcp<rive::RenderFont>(new SkiaRenderFont(std::move(tf)));
+}
+
+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;
+}
diff --git a/skia/viewer/src/main.cpp b/skia/viewer/src/main.cpp
index 7901254..50a170c 100644
--- a/skia/viewer/src/main.cpp
+++ b/skia/viewer/src/main.cpp
@@ -45,6 +45,8 @@
sk_sp<SkImage> gImage;
+extern void drawtext(rive::Factory*, rive::Renderer*);
+
static void delete_file() {
stateMachineIndex = -1;
animationIndex = -1;
@@ -347,6 +349,10 @@
canvas->drawImage(gImage, 0, 0);
}
}
+ if (true) {
+ rive::SkiaRenderer renderer(canvas);
+ drawtext(&skiaFactory, &renderer);
+ }
context->flush();
ImGui_ImplOpenGL3_NewFrame();
diff --git a/skia/viewer/src/skia_rive_fontmgr.hpp b/skia/viewer/src/skia_rive_fontmgr.hpp
new file mode 100644
index 0000000..9777b11
--- /dev/null
+++ b/skia/viewer/src/skia_rive_fontmgr.hpp
@@ -0,0 +1,22 @@
+/*
+ * Copyright 2022 Rive
+ */
+
+#ifndef _RIVE_SKIA_FONTMGR_HPP_
+#define _RIVE_SKIA_FONTMGR_HPP_
+
+#include "rive/refcnt.hpp"
+#include "rive/span.hpp"
+#include "rive/render_text.hpp"
+
+// This is a working model for how we might extend Factory.hpp
+class SkiaRiveFontMgr {
+public:
+ rive::rcp<rive::RenderFont> decodeFont(rive::Span<const uint8_t>);
+ rive::rcp<rive::RenderFont> findFont(const char name[]);
+ std::vector<rive::RenderGlyphRun> shapeText(rive::Span<const rive::Unichar> text,
+ rive::Span<const rive::RenderTextRun> runs);
+
+};
+
+#endif