Compare commits

...

2 Commits

Author SHA1 Message Date
bjorn fa2bd2b05c Shuffle around Rasterizer;
Shorter names, ditch linegap
2022-06-28 20:18:45 -07:00
bjorn 90092c55e4 Font: ignore \r; Fix uv adjustment; Ignore linegap; 2022-06-28 16:28:14 -07:00
5 changed files with 92 additions and 142 deletions

View File

@ -46,13 +46,6 @@ static int l_lovrRasterizerHasGlyphs(lua_State* L) {
return 1;
}
static int l_lovrRasterizerGetHeight(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float height = lovrRasterizerGetHeight(rasterizer);
lua_pushnumber(L, height);
return 1;
}
static int l_lovrRasterizerGetAscent(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float ascent = lovrRasterizerGetAscent(rasterizer);
@ -67,13 +60,6 @@ static int l_lovrRasterizerGetDescent(lua_State* L) {
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);
@ -95,6 +81,22 @@ static uint32_t luax_checkcodepoint(lua_State* L, int index) {
}
}
static int l_lovrRasterizerGetAdvance(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float advance = lovrRasterizerGetAdvance(rasterizer, codepoint);
lua_pushnumber(L, advance);
return 1;
}
static int l_lovrRasterizerGetBearing(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
float bearing = lovrRasterizerGetBearing(rasterizer, codepoint);
lua_pushnumber(L, bearing);
return 1;
}
static int l_lovrRasterizerGetKerning(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t prev = luax_checkcodepoint(L, 2);
@ -104,71 +106,44 @@ static int l_lovrRasterizerGetKerning(lua_State* L) {
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);
float box[4];
if (lua_isnoneornil(L, 2)) {
static void luax_getboundingbox(lua_State* L, int index, Rasterizer* rasterizer, float box[4]) {
if (lua_isnoneornil(L, index)) {
lovrRasterizerGetBoundingBox(rasterizer, box);
} else {
uint32_t codepoint = luax_checkcodepoint(L, 2);
uint32_t codepoint = luax_checkcodepoint(L, index);
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
}
}
static int l_lovrRasterizerGetWidth(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float box[4];
luax_getboundingbox(L, 2, rasterizer, box);
lua_pushnumber(L, box[2] - box[0]);
return 1;
}
static int l_lovrRasterizerGetGlyphHeight(lua_State* L) {
static int l_lovrRasterizerGetHeight(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float box[4];
if (lua_isnoneornil(L, 2)) {
lovrRasterizerGetBoundingBox(rasterizer, box);
} else {
uint32_t codepoint = luax_checkcodepoint(L, 2);
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
}
luax_getboundingbox(L, 2, rasterizer, box);
lua_pushnumber(L, box[3] - box[1]);
return 1;
}
static int l_lovrRasterizerGetGlyphDimensions(lua_State* L) {
static int l_lovrRasterizerGetDimensions(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float box[4];
if (lua_isnoneornil(L, 2)) {
lovrRasterizerGetBoundingBox(rasterizer, box);
} else {
uint32_t codepoint = luax_checkcodepoint(L, 2);
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
}
luax_getboundingbox(L, 2, rasterizer, box);
lua_pushnumber(L, box[2] - box[0]);
lua_pushnumber(L, box[3] - box[1]);
return 2;
}
static int l_lovrRasterizerGetGlyphBoundingBox(lua_State* L) {
static int l_lovrRasterizerGetBoundingBox(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
float box[4];
if (lua_isnoneornil(L, 2)) {
lovrRasterizerGetBoundingBox(rasterizer, box);
} else {
uint32_t codepoint = luax_checkcodepoint(L, 2);
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
}
luax_getboundingbox(L, 2, rasterizer, box);
lua_pushnumber(L, box[0]);
lua_pushnumber(L, box[1]);
lua_pushnumber(L, box[2]);
@ -201,25 +176,27 @@ static void onCurve3D(void* context, uint32_t degree, float* points) {
lua_rawseti(L, -2, luax_len(L, -2) + 1);
}
static int l_lovrRasterizerGetGlyphCurves(lua_State* L) {
static int l_lovrRasterizerGetCurves(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);
lovrRasterizerGetCurves(rasterizer, codepoint, three ? onCurve3D : onCurve2D, L);
return 1;
}
static int l_lovrRasterizerGetGlyphImage(lua_State* L) {
static int l_lovrRasterizerNewImage(lua_State* L) {
Rasterizer* rasterizer = luax_checktype(L, 1, Rasterizer);
uint32_t codepoint = luax_checkcodepoint(L, 2);
double spread = luaL_optnumber(L, 3, 4.);
uint32_t padding = (uint32_t) ceil(spread / 2.);
float box[4];
lovrRasterizerGetGlyphBoundingBox(rasterizer, codepoint, box);
uint32_t width = 2 + (uint32_t) ceilf(box[2] - box[0]);
uint32_t height = 2 + (uint32_t) ceilf(box[3] - box[1]);
uint32_t width = 2 * padding + (uint32_t) ceilf(box[2] - box[0]);
uint32_t height = 2 * padding + (uint32_t) ceilf(box[3] - box[1]);
Image* image = lovrImageCreateRaw(width, height, FORMAT_RGBA32F);
void* pixels = lovrImageGetLayerData(image, 0, 0);
lovrRasterizerGetGlyphPixels(rasterizer, codepoint, pixels, width, height, 4.);
lovrRasterizerGetPixels(rasterizer, codepoint, pixels, width, height, spread);
luax_pushtype(L, Image, image);
lovrRelease(image, lovrImageDestroy);
return 1;
@ -229,19 +206,17 @@ const luaL_Reg lovrRasterizer[] = {
{ "getFontSize", l_lovrRasterizerGetFontSize },
{ "getGlyphCount", l_lovrRasterizerGetGlyphCount },
{ "hasGlyphs", l_lovrRasterizerHasGlyphs },
{ "getHeight", l_lovrRasterizerGetHeight },
{ "getAscent", l_lovrRasterizerGetAscent },
{ "getDescent", l_lovrRasterizerGetDescent },
{ "getLineGap", l_lovrRasterizerGetLineGap },
{ "getLeading", l_lovrRasterizerGetLeading },
{ "getAdvance", l_lovrRasterizerGetAdvance },
{ "getBearing", l_lovrRasterizerGetBearing },
{ "getKerning", l_lovrRasterizerGetKerning },
{ "getGlyphAdvance", l_lovrRasterizerGetGlyphAdvance },
{ "getGlyphBearing", l_lovrRasterizerGetGlyphBearing },
{ "getGlyphWidth", l_lovrRasterizerGetGlyphWidth },
{ "getGlyphHeight", l_lovrRasterizerGetGlyphHeight },
{ "getGlyphDimensions", l_lovrRasterizerGetGlyphDimensions },
{ "getGlyphBoundingBox", l_lovrRasterizerGetGlyphBoundingBox },
{ "getGlyphCurves", l_lovrRasterizerGetGlyphCurves },
{ "getGlyphImage", l_lovrRasterizerGetGlyphImage },
{ "getWidth", l_lovrRasterizerGetWidth },
{ "getHeight", l_lovrRasterizerGetHeight },
{ "getDimensions", l_lovrRasterizerGetDimensions },
{ "getBoundingBox", l_lovrRasterizerGetBoundingBox },
{ "getCurves", l_lovrRasterizerGetCurves },
{ "newImage", l_lovrRasterizerNewImage },
{ NULL, NULL }
};

View File

@ -40,15 +40,6 @@ static int l_lovrFontSetLineSpacing(lua_State* L) {
return 0;
}
static int l_lovrFontGetHeight(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float height = lovrRasterizerGetHeight(info->rasterizer);
lua_pushnumber(L, height / density);
return 1;
}
static int l_lovrFontGetAscent(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
@ -67,21 +58,12 @@ static int l_lovrFontGetDescent(lua_State* L) {
return 1;
}
static int l_lovrFontGetLineGap(lua_State* L) {
static int l_lovrFontGetHeight(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float lineGap = lovrRasterizerGetLineGap(info->rasterizer);
lua_pushnumber(L, lineGap / density);
return 1;
}
static int l_lovrFontGetLeading(lua_State* L) {
Font* font = luax_checktype(L, 1, Font);
const FontInfo* info = lovrFontGetInfo(font);
float density = lovrFontGetPixelDensity(font);
float leading = lovrRasterizerGetLeading(info->rasterizer);
lua_pushnumber(L, leading / density);
float height = lovrRasterizerGetLeading(info->rasterizer);
lua_pushnumber(L, height / density);
return 1;
}
@ -91,10 +73,8 @@ const luaL_Reg lovrFont[] = {
{ "setPixelDensity", l_lovrFontSetPixelDensity },
{ "getLineSpacing", l_lovrFontGetLineSpacing },
{ "setLineSpacing", l_lovrFontSetLineSpacing },
{ "getHeight", l_lovrFontGetHeight },
{ "getAscent", l_lovrFontGetAscent },
{ "getDescent", l_lovrFontGetDescent },
{ "getLineGap", l_lovrFontGetLineGap },
{ "getLeading", l_lovrFontGetLeading },
{ "getHeight", l_lovrFontGetHeight },
{ NULL, NULL }
};

View File

@ -9,13 +9,12 @@
struct Rasterizer {
uint32_t ref;
struct Blob* blob;
float size;
float scale;
float ascent;
float descent;
float lineGap;
float leading;
struct Blob* blob;
stbtt_fontinfo font;
};
@ -35,11 +34,11 @@ Rasterizer* lovrRasterizerCreate(Blob* blob, float size) {
rasterizer->size = size;
rasterizer->scale = stbtt_ScaleForMappingEmToPixels(font, size);
// Even though line gap is a thing, it's usually zero so we pretend it isn't real
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;
@ -55,15 +54,6 @@ float lovrRasterizerGetFontSize(Rasterizer* rasterizer) {
return rasterizer->size;
}
void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]) {
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(&rasterizer->font, &x0, &y0, &x1, &y1);
box[0] = x0 * rasterizer->scale;
box[1] = y0 * rasterizer->scale;
box[2] = x1 * rasterizer->scale;
box[3] = y1 * rasterizer->scale;
}
uint32_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer) {
return rasterizer->font.numGlyphs;
}
@ -85,8 +75,8 @@ bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t len
return true;
}
float lovrRasterizerGetHeight(Rasterizer* rasterizer) {
return rasterizer->ascent - rasterizer->descent;
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint) {
return stbtt_IsGlyphEmpty(&rasterizer->font, stbtt_FindGlyphIndex(&rasterizer->font, codepoint));
}
float lovrRasterizerGetAscent(Rasterizer* rasterizer) {
@ -97,30 +87,35 @@ float lovrRasterizerGetDescent(Rasterizer* rasterizer) {
return rasterizer->descent;
}
float lovrRasterizerGetLineGap(Rasterizer* rasterizer) {
return rasterizer->lineGap;
}
float lovrRasterizerGetLeading(Rasterizer* rasterizer) {
return rasterizer->leading;
}
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) {
float lovrRasterizerGetAdvance(Rasterizer* rasterizer, uint32_t codepoint) {
int advance, bearing;
stbtt_GetCodepointHMetrics(&rasterizer->font, codepoint, &advance, &bearing);
return advance * rasterizer->scale;
}
float lovrRasterizerGetGlyphBearing(Rasterizer* rasterizer, uint32_t codepoint) {
float lovrRasterizerGetBearing(Rasterizer* rasterizer, uint32_t codepoint) {
int advance, bearing;
stbtt_GetCodepointHMetrics(&rasterizer->font, codepoint, &advance, &bearing);
return bearing * rasterizer->scale;
}
float lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right) {
return stbtt_GetCodepointKernAdvance(&rasterizer->font, left, right) * rasterizer->scale;
}
void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]) {
int x0, y0, x1, y1;
stbtt_GetFontBoundingBox(&rasterizer->font, &x0, &y0, &x1, &y1);
box[0] = x0 * rasterizer->scale;
box[1] = y0 * rasterizer->scale;
box[2] = x1 * rasterizer->scale;
box[3] = y1 * 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);
@ -130,11 +125,7 @@ void lovrRasterizerGetGlyphBoundingBox(Rasterizer* rasterizer, uint32_t codepoin
box[3] = y1 * rasterizer->scale;
}
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint) {
return stbtt_IsGlyphEmpty(&rasterizer->font, stbtt_FindGlyphIndex(&rasterizer->font, codepoint));
}
bool lovrRasterizerGetGlyphCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context) {
bool lovrRasterizerGetCurves(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)) {
@ -176,10 +167,10 @@ bool lovrRasterizerGetGlyphCurves(Rasterizer* rasterizer, uint32_t codepoint, vo
return true;
}
bool lovrRasterizerGetGlyphPixels(Rasterizer* rasterizer, uint32_t codepoint, float* pixels, uint32_t width, uint32_t height, double spread) {
bool lovrRasterizerGetPixels(Rasterizer* rasterizer, uint32_t codepoint, float* pixels, uint32_t width, uint32_t height, double spread) {
int id = stbtt_FindGlyphIndex(&rasterizer->font, codepoint);
if (stbtt_IsGlyphEmpty(&rasterizer->font, id)) {
if (!id || stbtt_IsGlyphEmpty(&rasterizer->font, id)) {
return false;
}

View File

@ -11,19 +11,17 @@ typedef struct Rasterizer Rasterizer;
Rasterizer* lovrRasterizerCreate(struct Blob* blob, float size);
void lovrRasterizerDestroy(void* ref);
float lovrRasterizerGetFontSize(Rasterizer* rasterizer);
void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]);
uint32_t lovrRasterizerGetGlyphCount(Rasterizer* rasterizer);
bool lovrRasterizerHasGlyph(Rasterizer* rasterizer, uint32_t codepoint);
bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str, size_t length);
float lovrRasterizerGetHeight(Rasterizer* rasterizer);
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint);
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);
float lovrRasterizerGetAdvance(Rasterizer* rasterizer, uint32_t codepoint);
float lovrRasterizerGetBearing(Rasterizer* rasterizer, uint32_t codepoint);
float lovrRasterizerGetKerning(Rasterizer* rasterizer, uint32_t left, uint32_t right);
void lovrRasterizerGetBoundingBox(Rasterizer* rasterizer, float box[4]);
void lovrRasterizerGetGlyphBoundingBox(Rasterizer* rasterizer, uint32_t codepoint, float box[4]);
bool lovrRasterizerIsGlyphEmpty(Rasterizer* rasterizer, uint32_t codepoint);
bool lovrRasterizerGetGlyphCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context);
bool lovrRasterizerGetGlyphPixels(Rasterizer* rasterizer, uint32_t codepoint, float* pixels, uint32_t width, uint32_t height, double spread);
bool lovrRasterizerGetCurves(Rasterizer* rasterizer, uint32_t codepoint, void (*fn)(void* context, uint32_t degree, float* points), void* context);
bool lovrRasterizerGetPixels(Rasterizer* rasterizer, uint32_t codepoint, float* pixels, uint32_t width, uint32_t height, double spread);

View File

@ -1776,7 +1776,7 @@ Font* lovrFontCreate(FontInfo* info) {
map_init(&font->glyphLookup, 36);
map_init(&font->kerning, 36);
font->pixelDensity = lovrRasterizerGetHeight(info->rasterizer);
font->pixelDensity = lovrRasterizerGetLeading(info->rasterizer);
font->lineSpacing = 1.f;
font->padding = (uint32_t) ceil(info->spread / 2.);
@ -1839,7 +1839,7 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
Glyph* glyph = &font->glyphs.data[font->glyphs.length++];
glyph->codepoint = codepoint;
glyph->advance = lovrRasterizerGetGlyphAdvance(font->info.rasterizer, codepoint);
glyph->advance = lovrRasterizerGetAdvance(font->info.rasterizer, codepoint);
if (lovrRasterizerIsGlyphEmpty(font->info.rasterizer, codepoint)) {
memset(glyph->box, 0, sizeof(glyph->box));
@ -1956,6 +1956,11 @@ static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, ColoredString* s
const char* str = strings[i].string;
const char* end = strings[i].string + strings[i].length;
while ((bytes = utf8_decode(str, end, &codepoint)) > 0) {
if (codepoint == ' ' || codepoint == '\n' || codepoint == '\r' || codepoint== '\t') {
str += bytes;
continue;
}
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
if (glyph->box[2] - glyph->box[0] != 0.f) {
vertices[0].uv.u = glyph->uv[0];
@ -1991,7 +1996,7 @@ static void lovrFontUploadNewGlyphs(Font* font, uint32_t start, ColoredString* s
uint32_t stack = tempPush();
float* pixels = tempAlloc(w * h * 4 * sizeof(float));
lovrRasterizerGetGlyphPixels(font->info.rasterizer, glyph->codepoint, pixels, w, h, font->info.spread);
lovrRasterizerGetPixels(font->info.rasterizer, glyph->codepoint, pixels, w, h, font->info.spread);
uint8_t* dst = gpu_map(scratchpad, w * h * 4 * sizeof(uint8_t), 4, GPU_MAP_WRITE);
float* src = pixels;
@ -3424,7 +3429,6 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
float x = 0.f;
float y = 0.f;
float leading = lovrRasterizerGetLeading(font->info.rasterizer) * font->lineSpacing;
float height = lovrRasterizerGetHeight(font->info.rasterizer);
float ascent = lovrRasterizerGetAscent(font->info.rasterizer);
float scale = 1.f / font->pixelDensity;
wrap /= scale;
@ -3458,6 +3462,9 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
previous = '\0';
str += bytes;
continue;
} else if (codepoint == '\r') {
str += bytes;
continue;
}
Glyph* glyph = lovrFontGetGlyph(font, codepoint);
@ -3511,8 +3518,7 @@ void lovrPassText(Pass* pass, Font* font, ColoredString* strings, uint32_t count
lovrFontUploadNewGlyphs(font, originalGlyphCount, strings, count, vertices);
mat4_scale(transform, scale, scale, scale);
float totalHeight = height + leading * (lineCount - 1);
mat4_translate(transform, 0.f, -ascent + valign / 2.f * totalHeight, 0.f);
mat4_translate(transform, 0.f, -ascent + valign / 2.f * (leading * lineCount), 0.f);
uint16_t* indices;
lovrPassDraw(pass, &(Draw) {