blob: 8e5570f98640b32e851e99c1c4004d9e7097b4c0 [file] [log] [blame]
/*
* Copyright 2022 Rive
*/
#include "renderfont_hb.hpp"
#include "rive/factory.hpp"
#include "rive/render_text.hpp"
#include "renderer_utils.hpp"
#include "hb.h"
#include "hb-ot.h"
rive::rcp<rive::RenderFont> HBRenderFont::Decode(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;
}
//////////////
constexpr int kStdScale = 2048;
constexpr float gInvScale = 1.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 * gInvScale, -y * gInvScale);
}
void rpath_line_to(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, float x1, float y1, void*) {
((rive::RawPath*)rpath)->lineTo(x1 * gInvScale, -y1 * gInvScale);
}
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 * gInvScale, -y1 * gInvScale,
x2 * gInvScale, -y2 * gInvScale);
}
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 * gInvScale, -y1 * gInvScale,
x2 * gInvScale, -y2 * gInvScale,
x3 * gInvScale, -y3 * gInvScale);
}
void rpath_close(hb_draw_funcs_t*, void* rpath, hb_draw_state_t*, void*) {
((rive::RawPath*)rpath)->close();
}
}
static rive::RenderFont::LineMetrics make_lmx(hb_font_t* font) {
// premable on font...
hb_ot_font_set_funcs(font);
hb_font_set_scale(font, kStdScale, kStdScale);
hb_font_extents_t extents;
hb_font_get_h_extents(font, &extents);
return {-extents.ascender * gInvScale, -extents.descender * gInvScale};
}
HBRenderFont::HBRenderFont(hb_font_t* font) :
RenderFont(make_lmx(font)),
m_Font(font) // we just take ownership, no need to call reference()
{
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);
}
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 };
// printf("[%d] %08X %g %g %g\n", 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);
std::vector<rive::RenderFont::Coord> coords(length);
for (unsigned i = 0; i < length; ++i) {
coords[i] = { axes[i].tag, values[i] };
}
return coords;
}
rive::rcp<rive::RenderFont> HBRenderFont::makeAtCoords(rive::Span<const Coord> coords) const {
AutoSTArray<16, hb_variation_t> vars(coords.size());
for (size_t i = 0; i < coords.size(); ++i) {
vars[i] = {coords[i].axis, coords[i].value};
}
auto font = hb_font_create_sub_font(m_Font);
hb_font_set_variations(font, vars.data(), vars.size());
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;
}
///////////////////////////////////////////////////////////
std::vector<rive::RenderGlyphRun>
HBRenderFont::onShapeText(rive::Span<const rive::Unichar> text,
rive::Span<const rive::RenderTextRun> truns) const {
std::vector<rive::RenderGlyphRun> gruns;
gruns.reserve(truns.size());
/////////////////
const hb_feature_t features[] = {
{ 'liga', 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
{ 'dlig', 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
{ 'kern', 1, HB_FEATURE_GLOBAL_START, HB_FEATURE_GLOBAL_END },
};
constexpr int numFeatures = sizeof(features) / sizeof(features[0]);
uint32_t unicharIndex = 0;
rive::Vec2D origin = {0, 0};
for (const auto& tr : truns) {
hb_buffer_t *buf = hb_buffer_create();
hb_buffer_add_utf32(buf, (const uint32_t*)&text[unicharIndex], tr.unicharCount, 0, tr.unicharCount);
hb_buffer_set_direction(buf, HB_DIRECTION_LTR);
hb_buffer_set_script(buf, HB_SCRIPT_LATIN);
hb_buffer_set_language(buf, hb_language_from_string("en", -1));
auto hbfont = (HBRenderFont*)tr.font.get();
hb_shape(hbfont->m_Font, buf, features, numFeatures);
unsigned int glyph_count;
hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count);
hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count);
// todo: check for missing glyphs, and perform font-substitution
rive::RenderGlyphRun gr;
gr.font = tr.font;
gr.size = tr.size;
gr.glyphs.resize(glyph_count);
gr.textOffsets.resize(glyph_count);
gr.xpos.resize(glyph_count + 1);
const float scale = tr.size / kStdScale;
for (unsigned int i = 0; i < glyph_count; i++) {
// hb_position_t x_offset = glyph_pos[i].x_offset;
// hb_position_t y_offset = glyph_pos[i].y_offset;
gr.glyphs[i] = (uint16_t)glyph_info[i].codepoint;
gr.textOffsets[i] = unicharIndex + glyph_info[i].cluster;
gr.xpos[i] = origin.x;
origin.x += glyph_pos[i].x_advance * scale;
}
gr.xpos[glyph_count] = origin.x;
gruns.push_back(std::move(gr));
unicharIndex += tr.unicharCount;
hb_buffer_destroy(buf);
}
return gruns;
}