Start updating Rasterizer;

This commit is contained in:
bjorn 2022-06-17 17:43:58 -07:00
parent cb121d3d36
commit 717f95f6bd
3 changed files with 267 additions and 231 deletions

View File

@ -4,78 +4,202 @@
#include <lua.h>
#include <lauxlib.h>
static int l_lovrRasterizerGetHeight(lua_State* L) {
static int l_lovrRasterizerGetFontSize(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetHeight(rasterizer));
return 1;
}
static int l_lovrRasterizerGetAdvance(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetAdvance(rasterizer));
return 1;
}
static int l_lovrRasterizerGetAscent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetAscent(rasterizer));
return 1;
}
static int l_lovrRasterizerGetDescent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetDescent(rasterizer));
return 1;
}
static int l_lovrRasterizerGetLineHeight(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetHeight(rasterizer) * 1.25f);
float size = lovrRasterizerGetFontSize(rasterizer);
lua_pushnumber(L, size);
return 1;
}
static int l_lovrRasterizerGetGlyphCount(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
lua_pushinteger(L, lovrRasterizerGetGlyphCount(rasterizer));
uint32_t count = lovrRasterizerGetGlyphCount(rasterizer);
lua_pushinteger(L, count);
return 1;
}
static int l_lovrRasterizerHasGlyphs(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
bool hasGlyphs = true;
for (int i = 2; i <= lua_gettop(L); i++) {
if (lua_type(L, i) == LUA_TSTRING) {
hasGlyphs &= lovrRasterizerHasGlyphs(rasterizer, lua_tostring(L, i));
} else {
hasGlyphs &= lovrRasterizerHasGlyph(rasterizer, luax_checku32(L, i));
size_t length;
const char* str;
switch (lua_type(L, i)) {
case LUA_TSTRING:
str = lua_tolstring(L, i, &length);
if (!lovrRasterizerHasGlyphs(rasterizer, str, length)) {
lua_pushboolean(L, false);
return 1;
}
break;
case LUA_TNUMBER:
if (!lovrRasterizerHasGlyph(rasterizer, luax_checku32(L, i))) {
lua_pushboolean(L, false);
return 1;
}
break;
default: return luax_typeerror(L, i, "string or number");
}
}
lua_pushboolean(L, hasGlyphs);
lua_pushboolean(L, true);
return 1;
}
static int l_lovrRasterizerGetWidth(lua_State* L) {
static int l_lovrRasterizerGetAscent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float ascent = lovrRasterizerGetAscent(rasterizer);
lua_pushnumber(L, ascent);
return 1;
}
static int l_lovrRasterizerGetDescent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float descent = lovrRasterizerGetDescent(rasterizer);
lua_pushnumber(L, descent);
return 1;
}
static int l_lovrRasterizerGetLineGap(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float lineGap = lovrRasterizerGetLineGap(rasterizer);
lua_pushnumber(L, lineGap);
return 1;
}
static int l_lovrRasterizerGetLeading(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float leading = lovrRasterizerGetLeading(rasterizer);
lua_pushnumber(L, leading);
return 1;
}
static uint32_t luax_checkcodepoint(lua_State* L, int index) {
size_t length;
const char* string = luaL_checklstring(L, 2, &length);
float wrap = luax_optfloat(L, 4, 0.f);
float width, lastLineWidth, height;
uint32_t lineCount, glyphCount;
lovrRasterizerMeasure(rasterizer, string, length, wrap, &width, &lastLineWidth, &height, &lineCount, &glyphCount);
lua_pushnumber(L, width);
lua_pushnumber(L, lineCount + 1);
lua_pushnumber(L, lastLineWidth);
const char* str;
uint32_t codepoint;
switch (lua_type(L, index)) {
case LUA_TSTRING:
str = lua_tolstring(L, index, &length);
return utf8_decode(str, str + length, &codepoint) ? codepoint : 0;
case LUA_TNUMBER:
return luax_checku32(L, index);
default: return luax_typeerror(L, index, "string or number"), 0;
}
}
static int l_lovrRasterizerGetKerning(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t prev = luax_checkcodepoint(L, 2);
uint32_t next = luax_checkcodepoint(L, 3);
float kerning = lovrRasterizerGetKerning(rasterizer, prev, next);
lua_pushnumber(L, kerning);
return 1;
}
static int l_lovrRasterizerGetGlyphAdvance(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float advance = lovrRasterizerGetGlyphAdvance(rasterizer, codepoint);
lua_pushnumber(L, advance);
return 1;
}
static int l_lovrRasterizerGetGlyphBearing(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float bearing = lovrRasterizerGetGlyphBearing(rasterizer, codepoint);
lua_pushnumber(L, bearing);
return 1;
}
static int l_lovrRasterizerGetGlyphWidth(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float box[4];
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
lua_pushnumber(L, box[2] - box[0]);
return 1;
}
static int l_lovrRasterizerGetGlyphHeight(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float box[4];
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
lua_pushnumber(L, box[3] - box[1]);
return 1;
}
static int l_lovrRasterizerGetGlyphDimensions(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float box[4];
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
lua_pushnumber(L, box[2] - box[0]);
lua_pushnumber(L, box[3] - box[1]);
return 2;
}
static int l_lovrRasterizerGetGlyphBoundingBox(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float box[4];
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
lua_pushnumber(L, box[0]);
lua_pushnumber(L, box[1]);
lua_pushnumber(L, box[2]);
lua_pushnumber(L, box[3]);
return 4;
}
static void onCurve2D(void* context, uint32_t degree, float* points) {
lua_State* L = context;
uint32_t count = (degree + 1) * 2;
lua_createtable(L, count, 0);
for (uint32_t i = 0; i < count; i++) {
lua_pushnumber(L, points[i]);
lua_rawseti(L, -2, i + 1);
}
lua_rawseti(L, -2, luax_len(L, -2) + 1);
}
static void onCurve3D(void* context, uint32_t degree, float* points) {
lua_State* L = context;
lua_createtable(L, (degree + 1) * 3, 0);
for (uint32_t i = 0; i < degree + 1; i++) {
lua_pushnumber(L, points[2 * i + 0]);
lua_rawseti(L, -2, 3 * i + 1);
lua_pushnumber(L, points[2 * i + 1]);
lua_rawseti(L, -2, 3 * i + 2);
lua_pushnumber(L, 0.);
lua_rawseti(L, -2, 3 * i + 3);
}
lua_rawseti(L, -2, luax_len(L, -2) + 1);
}
static int l_lovrRasterizerGetGlyphCurves(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
bool three = lua_toboolean(L, 3);
lua_newtable(L);
lovrRasterizerGetGlyphCurves(rasterizer, codepoint, three ? onCurve3D : onCurve2D, L);
return 1;
}
const luaL_Reg lovrRasterizer[] = {
{ "getHeight", l_lovrRasterizerGetHeight },
{ "getAdvance", l_lovrRasterizerGetAdvance },
{ "getAscent", l_lovrRasterizerGetAscent },
{ "getDescent", l_lovrRasterizerGetDescent },
{ "getLineHeight", l_lovrRasterizerGetLineHeight },
{ "getFontSize", l_lovrRasterizerGetFontSize },
{ "getGlyphCount", l_lovrRasterizerGetGlyphCount },
{ "hasGlyphs", l_lovrRasterizerHasGlyphs },
{ "getWidth", l_lovrRasterizerGetWidth },
{ "getAscent", l_lovrRasterizerGetAscent },
{ "getDescent", l_lovrRasterizerGetDescent },
{ "getLineGap", l_lovrRasterizerGetLineGap },
{ "getLeading", l_lovrRasterizerGetLeading },
{ "getKerning", l_lovrRasterizerGetKerning },
{ "getGlyphAdvance", l_lovrRasterizerGetGlyphAdvance },
{ "getGlyphBearing", l_lovrRasterizerGetGlyphBearing },
{ "getGlyphWidth", l_lovrRasterizerGetGlyphWidth },
{ "getGlyphHeight", l_lovrRasterizerGetGlyphHeight },
{ "getGlyphDimensions", l_lovrRasterizerGetGlyphDimensions },
{ "getGlyphBoundingBox", l_lovrRasterizerGetGlyphBoundingBox },
{ "getGlyphCurves", l_lovrRasterizerGetGlyphCurves },
{ NULL, NULL }
};

View File

@ -1,25 +1,21 @@
#include "data/rasterizer.h"
#include "data/blob.h"
#include "data/image.h"
#include "util.h"
#include "VarelaRound.ttf.h"
#include "lib/stb/stb_truetype.h"
#include <msdfgen-c.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
struct Rasterizer {
uint32_t ref;
stbtt_fontinfo font;
struct Blob* blob;
float size;
float scale;
int glyphCount;
int height;
int advance;
int ascent;
int descent;
float ascent;
float descent;
float lineGap;
float leading;
stbtt_fontinfo font;
};
Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
@ -37,17 +33,13 @@ Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
rasterizer->blob = blob;
rasterizer->size = size;
rasterizer->scale = stbtt_ScaleForMappingEmToPixels(font, size);
rasterizer->glyphCount = font->numGlyphs;
int ascent, descent, linegap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &linegap);
rasterizer->ascent = roundf(ascent * rasterizer->scale);
rasterizer->descent = roundf(descent * rasterizer->scale);
rasterizer->height = roundf((ascent - descent + linegap) * rasterizer->scale);
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(font, &x0, &y0, &x1, &y1);
rasterizer->advance = roundf(x1 * rasterizer->scale);
int ascent, descent, lineGap;
stbtt_GetFontVMetrics(font, &ascent, &descent, &lineGap);
rasterizer->ascent = ascent * rasterizer->scale;
rasterizer->descent = descent * rasterizer->scale;
rasterizer->lineGap = lineGap * rasterizer->scale;
rasterizer->leading = (ascent - descent + lineGap) * rasterizer->scale;
return rasterizer;
}
@ -58,96 +50,108 @@ void lovrRasterizerDestroy(void* ref) {
free(rasterizer);
}
float lovrRasterizerGetSize(Rasterizer* rasterizer) {
float lovrRasterizerGetFontSize(Rasterizer* rasterizer) {
return rasterizer->size;
}
int lovrRasterizerGetGlyphCount(Rasterizer* rasterizer) {
return rasterizer->glyphCount;
uint32_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer) {
return rasterizer->font.numGlyphs;
}
int lovrRasterizerGetHeight(Rasterizer* rasterizer) {
return rasterizer->height;
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t codepoint) {
return stbtt_FindGlyphIndex(&rasterizer->font, codepoint) != 0;
}
int lovrRasterizerGetAdvance(Rasterizer* rasterizer) {
return rasterizer->advance;
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t length) {
size_t bytes;
uint32_t codepoint;
const char* end = str + length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (!lovrRasterizerHasGlyph(rasterizer, codepoint)) {
return false;
}
str += bytes;
}
return true;
}
int lovrRasterizerGetAscent(Rasterizer* rasterizer) {
float lovrRasterizerGetAscent(Rasterizer* rasterizer) {
return rasterizer->ascent;
}
int lovrRasterizerGetDescent(Rasterizer* rasterizer) {
float lovrRasterizerGetDescent(Rasterizer* rasterizer) {
return rasterizer->descent;
}
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t character) {
return stbtt_FindGlyphIndex(&rasterizer->font, character) != 0;
float lovrRasterizerGetLineGap(Rasterizer* rasterizer) {
return rasterizer->lineGap;
}
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
const char* end = str + strlen(str);
unsigned int codepoint;
size_t bytes;
bool hasGlyphs = true;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
hasGlyphs &= lovrRasterizerHasGlyph(rasterizer, codepoint);
str += bytes;
}
return hasGlyphs;
float lovrRasterizerGetLeading(Rasterizer* rasterizer) {
return rasterizer->leading;
}
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, uint32_t padding, double spread, Glyph* glyph) {
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, character);
float lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t prev, uint32_t next) {
return stbtt_GetCodepointKernAdvance(&rasterizer->font, prev, next) * rasterizer->scale;
}
float lovrRasterizerGetGlyphAdvance(Rasterizer* rasterizer, uint32_t codepoint) {
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
stbtt_GetCodepointHMetrics(&rasterizer->font, codepoint, &advance, &bearing);
return advance * rasterizer->scale;
}
if (stbtt_IsGlyphEmpty(&rasterizer->font, glyphIndex)) {
memset(glyph, 0, sizeof(Glyph));
glyph->advance = roundf(advance * rasterizer->scale);
float lovrRasterizerGetGlyphBearing(Rasterizer* rasterizer, uint32_t codepoint) {
int advance, bearing;
stbtt_GetCodepointHMetrics(&rasterizer->font, codepoint, &advance, &bearing);
return bearing * rasterizer->scale;
}
void lovrRasterizerGetGlyphBoundingBox(Rasterizer* rasterizer, uint32_t codepoint, float box[4]) {
int x0, y0, x1, y1;
stbtt_GetCodepointBox(&rasterizer->font, codepoint, &x0, &y0, &x1, &y1);
box[0] = x0 * rasterizer->scale;
box[1] = y0 * rasterizer->scale;
box[2] = x1 * rasterizer->scale;
box[3] = y1 * rasterizer->scale;
}
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint) {
return stbtt_IsGlyphEmpty(&rasterizer->font, stbtt_FindGlyphIndex(&rasterizer->font, codepoint));
}
void lovrRasterizerGetGlyphCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context) {
uint32_t id = stbtt_FindGlyphIndex(&rasterizer->font, codepoint);
if (stbtt_IsGlyphEmpty(&rasterizer->font, id)) {
return;
}
// Trace glyph outline
stbtt_vertex* vertices;
int vertexCount = stbtt_GetGlyphShape(&rasterizer->font, glyphIndex, &vertices);
msShape* shape = msShapeCreate();
msContour* contour = NULL;
uint32_t count = stbtt_GetGlyphShape(&rasterizer->font, id, &vertices);
float x = 0.f;
float y = 0.f;
for (int i = 0; i < vertexCount; i++) {
for (uint32_t i = 0; i < count; i++) {
stbtt_vertex vertex = vertices[i];
float x2 = vertex.x * rasterizer->scale;
float y2 = vertex.y * rasterizer->scale;
switch (vertex.type) {
case STBTT_vmove:
contour = msShapeAddContour(shape);
break;
case STBTT_vline:
msContourAddLinearEdge(contour, x, y, x2, y2);
break;
case STBTT_vcurve: {
float cx = vertex.cx * rasterizer->scale;
float cy = vertex.cy * rasterizer->scale;
msContourAddQuadraticEdge(contour, x, y, cx, cy, x2, y2);
break;
}
case STBTT_vcubic: {
float cx1 = vertex.cx * rasterizer->scale;
float cy1 = vertex.cy * rasterizer->scale;
float cx2 = vertex.cx1 * rasterizer->scale;
float cy2 = vertex.cy1 * rasterizer->scale;
msContourAddCubicEdge(contour, x, y, cx1, cy1, cx2, cy2, x2, y2);
break;
}
if (vertex.type == STBTT_vline) {
float points[4] = { x, y, x2, y2 };
fn(context, 1, points);
} else if (vertex.type == STBTT_vcurve) {
float cx = vertex.cx * rasterizer->scale;
float cy = vertex.cy * rasterizer->scale;
float points[6] = { x, y, cx, cy, x2, y2 };
fn(context, 2, points);
} else if (vertex.type == STBTT_vcubic) {
float cx1 = vertex.cx * rasterizer->scale;
float cy1 = vertex.cy * rasterizer->scale;
float cx2 = vertex.cx1 * rasterizer->scale;
float cy2 = vertex.cy1 * rasterizer->scale;
float points[8] = { x, y, cx1, cy1, cx2, cy2, x2, y2 };
fn(context, 3, points);
}
x = x2;
@ -155,86 +159,4 @@ void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, uint32_
}
stbtt_FreeShape(&rasterizer->font, vertices);
int x0, y0, x1, y1;
stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1);
// Initialize glyph data
glyph->x = 0;
glyph->y = 0;
glyph->w = ceilf((x1 - x0) * rasterizer->scale);
glyph->h = ceilf((y1 - y0) * rasterizer->scale);
glyph->tw = glyph->w + 2 * padding;
glyph->th = glyph->h + 2 * padding;
glyph->dx = roundf(bearing * rasterizer->scale);
glyph->dy = roundf(y1 * rasterizer->scale);
glyph->advance = roundf(advance * rasterizer->scale);
glyph->data = lovrImageCreateRaw(glyph->tw, glyph->th, FORMAT_RGBA32F);
// Render SDF
float tx = (float) padding + -glyph->dx;
float ty = (float) padding + (float) glyph->h - glyph->dy;
msShapeNormalize(shape);
msEdgeColoringSimple(shape, 3., 0);
msGenerateMTSDF(lovrImageGetBlob(glyph->data)->data, glyph->tw, glyph->th, shape, spread, 1.f, 1.f, tx, ty);
msShapeDestroy(shape);
}
int32_t lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
return stbtt_GetCodepointKernAdvance(&rasterizer->font, left, right) * rasterizer->scale;
}
void lovrRasterizerMeasure(Rasterizer* rasterizer, const char* str, size_t length, float wrap, float* width, float* lastLineWidth, float* height, uint32_t* lineCount, uint32_t* glyphCount) {
float x = 0.f;
const char* end = str + length;
size_t bytes;
unsigned int previous = '\0';
unsigned int codepoint;
*width = 0.f;
*lastLineWidth = 0.f;
*lineCount = 0;
*glyphCount = 0;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == '\n' || (wrap && x > wrap && (codepoint == ' ' || previous == ' '))) {
*width = MAX(*width, x);
(*lineCount)++;
x = 0.f;
previous = '\0';
if (codepoint == ' ' || codepoint == '\n') {
str += bytes;
continue;
}
}
// Tabs
if (codepoint == '\t') {
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, ' ');
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
x += advance * 4.f;
str += bytes;
continue;
}
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, codepoint);
int advance, bearing;
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
int x0, y0, x1, y1;
if (stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1)) {
float w = ceilf((x1 - x0) * rasterizer->scale);
float h = ceilf((y1 - y0) * rasterizer->scale);
if (w > 0 && h > 0) {
(*glyphCount)++;
}
}
x += roundf(advance * rasterizer->scale) + lovrRasterizerGetKerning(rasterizer, previous, codepoint);
previous = codepoint;
str += bytes;
}
*width = MAX(*width, x);
*lastLineWidth = x;
*height = ((*lineCount + 1) * lovrRasterizerGetHeight(rasterizer));
}

View File

@ -7,30 +7,20 @@
struct Blob;
struct Image;
typedef struct {
uint32_t x;
uint32_t y;
uint32_t w;
uint32_t h;
uint32_t tw;
uint32_t th;
int32_t dx;
int32_t dy;
int32_t advance;
struct Image* data;
} Glyph;
typedef struct Rasterizer Rasterizer;
Rasterizer* lovrRasterizerCreate(struct Blob* blob, float size);
void lovrRasterizerDestroy(void* ref);
float lovrRasterizerGetSize(Rasterizer* rasterizer);
int lovrRasterizerGetGlyphCount(Rasterizer* rasterizer);
int lovrRasterizerGetHeight(Rasterizer* rasterizer);
int lovrRasterizerGetAdvance(Rasterizer* rasterizer);
int lovrRasterizerGetAscent(Rasterizer* rasterizer);
int lovrRasterizerGetDescent(Rasterizer* rasterizer);
bool lovrRasterizerHasGlyph(Rasterizer* fontData, uint32_t character);
bool lovrRasterizerHasGlyphs(Rasterizer* fontData, const char* str);
void lovrRasterizerLoadGlyph(Rasterizer* fontData, uint32_t character, uint32_t padding, double spread, Glyph* glyph);
int32_t lovrRasterizerGetKerning(Rasterizer* fontData, uint32_t left, uint32_t right);
void lovrRasterizerMeasure(Rasterizer* rasterizer, const char* str, size_t length, float wrap, float* width, float* lastLineWidth, float* height, uint32_t* lineCount, uint32_t* glyphCount);
float lovrRasterizerGetFontSize(Rasterizer* rasterizer);
uint32_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer);
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t codepoint);
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t length);
float lovrRasterizerGetAscent(Rasterizer* rasterizer);
float lovrRasterizerGetDescent(Rasterizer* rasterizer);
float lovrRasterizerGetLineGap(Rasterizer* rasterizer);
float lovrRasterizerGetLeading(Rasterizer* rasterizer);
float lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t prev, uint32_t next);
float lovrRasterizerGetGlyphAdvance(Rasterizer* rasterizer, uint32_t codepoint);
float lovrRasterizerGetGlyphBearing(Rasterizer* rasterizer, uint32_t codepoint);
void lovrRasterizerGetGlyphBoundingBox(Rasterizer* rasterizer, uint32_t codepoint, float box[4]);
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint);
void lovrRasterizerGetGlyphCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context);