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