mirror of https://github.com/bjornbytes/lovr.git
Improve MSDF font shader; Add spread/padding settings to Font;
This commit is contained in:
parent
7b136b914c
commit
0919da4091
|
@ -1232,6 +1232,8 @@ static int l_lovrGraphicsNewCanvas(lua_State* L) {
|
|||
|
||||
static int l_lovrGraphicsNewFont(lua_State* L) {
|
||||
Rasterizer* rasterizer = luax_totype(L, 1, Rasterizer);
|
||||
uint32_t padding = 2;
|
||||
double spread = 4.;
|
||||
|
||||
if (!rasterizer) {
|
||||
Blob* blob = NULL;
|
||||
|
@ -1239,16 +1241,23 @@ static int l_lovrGraphicsNewFont(lua_State* L) {
|
|||
|
||||
if (lua_type(L, 1) == LUA_TNUMBER || lua_isnoneornil(L, 1)) {
|
||||
size = luaL_optinteger(L, 1, 32);
|
||||
padding = luaL_optinteger(L, 2, padding);
|
||||
spread = luaL_optnumber(L, 3, spread);
|
||||
} else {
|
||||
blob = luax_readblob(L, 1, "Font");
|
||||
size = luaL_optinteger(L, 2, 32);
|
||||
padding = luaL_optinteger(L, 3, padding);
|
||||
spread = luaL_optnumber(L, 4, spread);
|
||||
}
|
||||
|
||||
rasterizer = lovrRasterizerCreate(blob, size);
|
||||
lovrRelease(blob, lovrBlobDestroy);
|
||||
} else {
|
||||
padding = luaL_optinteger(L, 2, padding);
|
||||
spread = luaL_optnumber(L, 3, spread);
|
||||
}
|
||||
|
||||
Font* font = lovrFontCreate(rasterizer);
|
||||
Font* font = lovrFontCreate(rasterizer, padding, spread);
|
||||
luax_pushtype(L, Font, font);
|
||||
lovrRelease(rasterizer, lovrRasterizerDestroy);
|
||||
lovrRelease(font, lovrFontDestroy);
|
||||
|
|
|
@ -99,10 +99,19 @@ bool lovrRasterizerHasGlyphs(Rasterizer* rasterizer, const char* str) {
|
|||
return hasGlyphs;
|
||||
}
|
||||
|
||||
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph* glyph) {
|
||||
void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, uint32_t padding, double spread, Glyph* glyph) {
|
||||
int glyphIndex = stbtt_FindGlyphIndex(&rasterizer->font, character);
|
||||
lovrAssert(glyphIndex, "No font glyph found for character code %d, try using Rasterizer:hasGlyphs", character);
|
||||
|
||||
int advance, bearing;
|
||||
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
|
||||
|
||||
if (stbtt_IsGlyphEmpty(&rasterizer->font, glyphIndex)) {
|
||||
memset(glyph, 0, sizeof(Glyph));
|
||||
glyph->advance = roundf(advance * rasterizer->scale);
|
||||
return;
|
||||
}
|
||||
|
||||
// Trace glyph outline
|
||||
stbtt_vertex* vertices;
|
||||
int vertexCount = stbtt_GetGlyphShape(&rasterizer->font, glyphIndex, &vertices);
|
||||
|
@ -148,32 +157,27 @@ void lovrRasterizerLoadGlyph(Rasterizer* rasterizer, uint32_t character, Glyph*
|
|||
|
||||
stbtt_FreeShape(&rasterizer->font, vertices);
|
||||
|
||||
int advance, bearing;
|
||||
stbtt_GetGlyphHMetrics(&rasterizer->font, glyphIndex, &advance, &bearing);
|
||||
|
||||
int x0, y0, x1, y1;
|
||||
stbtt_GetGlyphBox(&rasterizer->font, glyphIndex, &x0, &y0, &x1, &y1);
|
||||
|
||||
bool empty = stbtt_IsGlyphEmpty(&rasterizer->font, glyphIndex);
|
||||
|
||||
// Initialize glyph data
|
||||
glyph->x = 0;
|
||||
glyph->y = 0;
|
||||
glyph->w = empty ? 0 : ceilf((x1 - x0) * rasterizer->scale);
|
||||
glyph->h = empty ? 0 : ceilf((y1 - y0) * rasterizer->scale);
|
||||
glyph->tw = glyph->w + 2 * GLYPH_PADDING;
|
||||
glyph->th = glyph->h + 2 * GLYPH_PADDING;
|
||||
glyph->dx = empty ? 0 : roundf(bearing * rasterizer->scale);
|
||||
glyph->dy = empty ? 0 : roundf(y1 * rasterizer->scale);
|
||||
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 = lovrImageCreate(glyph->tw, glyph->th, NULL, 0, FORMAT_RGBA32F);
|
||||
|
||||
// Render SDF
|
||||
float tx = GLYPH_PADDING + -glyph->dx;
|
||||
float ty = GLYPH_PADDING + (float) glyph->h - glyph->dy;
|
||||
float tx = (float) padding + -glyph->dx;
|
||||
float ty = (float) padding + (float) glyph->h - glyph->dy;
|
||||
msShapeNormalize(shape);
|
||||
msEdgeColoringSimple(shape, 3., 0);
|
||||
msGenerateMTSDF(glyph->data->blob->data, glyph->tw, glyph->th, shape, 4.f, 1.f, 1.f, tx, ty);
|
||||
msGenerateMTSDF(glyph->data->blob->data, glyph->tw, glyph->th, shape, spread, 1.f, 1.f, tx, ty);
|
||||
msShapeDestroy(shape);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#define GLYPH_PADDING 1
|
||||
|
||||
struct Blob;
|
||||
struct Image;
|
||||
|
||||
|
@ -32,5 +30,5 @@ 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, Glyph* glyph);
|
||||
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);
|
||||
|
|
|
@ -23,6 +23,8 @@ struct Font {
|
|||
Texture* texture;
|
||||
FontAtlas atlas;
|
||||
map_t kerning;
|
||||
double spread;
|
||||
uint32_t padding;
|
||||
float lineHeight;
|
||||
float pixelDensity;
|
||||
bool flip;
|
||||
|
@ -47,24 +49,28 @@ static void lovrFontAddGlyph(Font* font, Glyph* glyph);
|
|||
static void lovrFontExpandTexture(Font* font);
|
||||
static void lovrFontCreateTexture(Font* font);
|
||||
|
||||
Font* lovrFontCreate(Rasterizer* rasterizer) {
|
||||
Font* lovrFontCreate(Rasterizer* rasterizer, uint32_t padding, double spread) {
|
||||
Font* font = calloc(1, sizeof(Font));
|
||||
lovrAssert(font, "Out of memory");
|
||||
font->ref = 1;
|
||||
|
||||
lovrRetain(rasterizer);
|
||||
font->rasterizer = rasterizer;
|
||||
font->padding = padding;
|
||||
font->spread = spread;
|
||||
font->lineHeight = 1.f;
|
||||
font->pixelDensity = (float) lovrRasterizerGetHeight(rasterizer);
|
||||
map_init(&font->kerning, 0);
|
||||
|
||||
// Atlas
|
||||
uint32_t padding = 1;
|
||||
font->atlas.x = padding;
|
||||
font->atlas.y = padding;
|
||||
font->atlas.width = 128;
|
||||
font->atlas.height = 128;
|
||||
font->atlas.padding = padding;
|
||||
// The atlas padding affects the padding of the edges of the atlas and the space between rows.
|
||||
// It is different from the main font->padding, which is the padding on each individual glyph.
|
||||
uint32_t atlasPadding = 1;
|
||||
font->atlas.x = atlasPadding;
|
||||
font->atlas.y = atlasPadding;
|
||||
font->atlas.width = 256;
|
||||
font->atlas.height = 256;
|
||||
font->atlas.padding = atlasPadding;
|
||||
arr_init(&font->atlas.glyphs, realloc);
|
||||
map_init(&font->atlas.glyphMap, 0);
|
||||
|
||||
|
@ -160,8 +166,8 @@ void lovrFontRender(Font* font, const char* str, size_t length, float wrap, Hori
|
|||
|
||||
// Triangles
|
||||
if (glyph->w > 0 && glyph->h > 0) {
|
||||
float x1 = cx + glyph->dx - GLYPH_PADDING;
|
||||
float y1 = cy + (glyph->dy + GLYPH_PADDING) * (flip ? -1.f : 1.f);
|
||||
float x1 = cx + glyph->dx - font->padding;
|
||||
float y1 = cy + (glyph->dy + font->padding) * (flip ? -1.f : 1.f);
|
||||
float x2 = x1 + glyph->tw;
|
||||
float y2 = y1 - glyph->th * (flip ? -1.f : 1.f);
|
||||
float s1 = glyph->x / u;
|
||||
|
@ -236,6 +242,14 @@ void lovrFontMeasure(Font* font, const char* str, size_t length, float wrap, flo
|
|||
*height = ((*lineCount + 1) * lovrRasterizerGetHeight(font->rasterizer) * font->lineHeight) * (font->flip ? -1 : 1);
|
||||
}
|
||||
|
||||
uint32_t lovrFontGetPadding(Font* font) {
|
||||
return font->padding;
|
||||
}
|
||||
|
||||
double lovrFontGetSpread(Font* font) {
|
||||
return font->spread;
|
||||
}
|
||||
|
||||
float lovrFontGetHeight(Font* font) {
|
||||
return lovrRasterizerGetHeight(font->rasterizer) / font->pixelDensity;
|
||||
}
|
||||
|
@ -302,7 +316,7 @@ static Glyph* lovrFontGetGlyph(Font* font, uint32_t codepoint) {
|
|||
if (index == MAP_NIL) {
|
||||
index = atlas->glyphs.length;
|
||||
arr_reserve(&atlas->glyphs, atlas->glyphs.length + 1);
|
||||
lovrRasterizerLoadGlyph(font->rasterizer, codepoint, &atlas->glyphs.data[atlas->glyphs.length++]);
|
||||
lovrRasterizerLoadGlyph(font->rasterizer, codepoint, font->padding, font->spread, &atlas->glyphs.data[atlas->glyphs.length++]);
|
||||
map_set(&atlas->glyphMap, hash, index);
|
||||
lovrFontAddGlyph(font, &atlas->glyphs.data[index]);
|
||||
}
|
||||
|
@ -374,7 +388,7 @@ static void lovrFontExpandTexture(Font* font) {
|
|||
// Could look into using glClearTexImage when supported to make this more efficient.
|
||||
static void lovrFontCreateTexture(Font* font) {
|
||||
lovrRelease(font->texture, lovrTextureDestroy);
|
||||
Image* image = lovrImageCreate(font->atlas.width, font->atlas.height, NULL, 0x0, FORMAT_RGB);
|
||||
Image* image = lovrImageCreate(font->atlas.width, font->atlas.height, NULL, 0x0, FORMAT_RG11B10F);
|
||||
font->texture = lovrTextureCreate(TEXTURE_2D, &image, 1, false, false, 0);
|
||||
lovrTextureSetFilter(font->texture, (TextureFilter) { .mode = FILTER_BILINEAR });
|
||||
lovrTextureSetWrap(font->texture, (TextureWrap) { .s = WRAP_CLAMP, .t = WRAP_CLAMP });
|
||||
|
|
|
@ -20,12 +20,14 @@ typedef enum {
|
|||
} VerticalAlign;
|
||||
|
||||
typedef struct Font Font;
|
||||
Font* lovrFontCreate(struct Rasterizer* rasterizer);
|
||||
Font* lovrFontCreate(struct Rasterizer* rasterizer, uint32_t padding, double spread);
|
||||
void lovrFontDestroy(void* ref);
|
||||
struct Rasterizer* lovrFontGetRasterizer(Font* font);
|
||||
struct Texture* lovrFontGetTexture(Font* font);
|
||||
void lovrFontRender(Font* font, const char* str, size_t length, float wrap, HorizontalAlign halign, float* vertices, uint16_t* indices, uint16_t baseVertex);
|
||||
void lovrFontMeasure(Font* font, const char* string, size_t length, float wrap, float* width, float* height, uint32_t* lineCount, uint32_t* glyphCount);
|
||||
uint32_t lovrFontGetPadding(Font* font);
|
||||
double lovrFontGetSpread(Font* font);
|
||||
float lovrFontGetHeight(Font* font);
|
||||
float lovrFontGetAscent(Font* font);
|
||||
float lovrFontGetDescent(Font* font);
|
||||
|
|
|
@ -51,6 +51,7 @@ typedef union {
|
|||
struct { DrawStyle style; ArcMode mode; float r1; float r2; int segments; } arc;
|
||||
struct { float r1; float r2; bool capped; int segments; } cylinder;
|
||||
struct { int segments; } sphere;
|
||||
struct { float spread; } text;
|
||||
struct { float u; float v; float w; float h; } fill;
|
||||
struct { uint32_t rangeStart; uint32_t rangeCount; uint32_t instances; float* pose; } mesh;
|
||||
} BatchParams;
|
||||
|
@ -467,7 +468,7 @@ Font* lovrGraphicsGetFont() {
|
|||
if (!state.font) {
|
||||
if (!state.defaultFont) {
|
||||
Rasterizer* rasterizer = lovrRasterizerCreate(NULL, 32);
|
||||
state.defaultFont = lovrFontCreate(rasterizer);
|
||||
state.defaultFont = lovrFontCreate(rasterizer, 1, 3.);
|
||||
lovrRelease(rasterizer, lovrRasterizerDestroy);
|
||||
}
|
||||
|
||||
|
@ -757,6 +758,13 @@ void lovrGraphicsFlush() {
|
|||
lovrShaderSetBlock(batch->draw.shader, "lovrModelBlock", state.buffers[STREAM_MODEL], batch->drawStart * bufferStride[STREAM_MODEL], MAX_DRAWS * bufferStride[STREAM_MODEL], ACCESS_READ);
|
||||
lovrShaderSetBlock(batch->draw.shader, "lovrColorBlock", state.buffers[STREAM_COLOR], batch->drawStart * bufferStride[STREAM_COLOR], MAX_DRAWS * bufferStride[STREAM_COLOR], ACCESS_READ);
|
||||
lovrShaderSetBlock(batch->draw.shader, "lovrFrameBlock", state.buffers[STREAM_FRAME], (state.head[STREAM_FRAME] - 1) * bufferStride[STREAM_FRAME], bufferStride[STREAM_FRAME], ACCESS_READ);
|
||||
if (batch->type == BATCH_TEXT) {
|
||||
Texture* texture = lovrMaterialGetTexture(batch->material, TEXTURE_DIFFUSE);
|
||||
uint32_t width = lovrTextureGetWidth(texture, 0);
|
||||
uint32_t height = lovrTextureGetHeight(texture, 0);
|
||||
float range[2] = { batch->params.text.spread / width, batch->params.text.spread / height };
|
||||
lovrShaderSetFloats(batch->draw.shader, "lovrSdfRange", range, 0, 2);
|
||||
}
|
||||
if (batch->draw.topology == DRAW_POINTS) {
|
||||
lovrShaderSetFloats(batch->draw.shader, "lovrPointSize", &state.pointSize, 0, 1);
|
||||
}
|
||||
|
@ -1277,6 +1285,7 @@ void lovrGraphicsPrint(const char* str, size_t length, mat4 transform, float wra
|
|||
uint16_t baseVertex;
|
||||
lovrGraphicsBatch(&(BatchRequest) {
|
||||
.type = BATCH_TEXT,
|
||||
.params.text.spread = lovrFontGetSpread(font),
|
||||
.topology = DRAW_TRIANGLES,
|
||||
.shader = SHADER_FONT,
|
||||
.pipeline = &pipeline,
|
||||
|
|
|
@ -344,14 +344,19 @@ const char* lovrPanoFragmentShader = ""
|
|||
"}";
|
||||
|
||||
const char* lovrFontFragmentShader = ""
|
||||
"uniform vec2 lovrSdfRange; \n"
|
||||
"float screenPxRange() { \n"
|
||||
" vec2 screenTexSize = vec2(1.) / fwidth(lovrTexCoord); \n"
|
||||
" return max(.5 * dot(lovrSdfRange, screenTexSize), 1.); \n"
|
||||
"} \n"
|
||||
"float median(float r, float g, float b) { \n"
|
||||
" return max(min(r, g), min(max(r, g), b)); \n"
|
||||
"} \n"
|
||||
"vec4 color(vec4 graphicsColor, sampler2D image, vec2 uv) { \n"
|
||||
" vec3 col = texture(lovrDiffuseTexture, lovrTexCoord).rgb; \n"
|
||||
" float sdf = median(col.r, col.g, col.b); \n"
|
||||
" float w = fwidth(sdf); \n"
|
||||
" float alpha = smoothstep(.5 - w, .5 + w, sdf); \n"
|
||||
" vec3 msd = texture(lovrDiffuseTexture, lovrTexCoord).rgb; \n"
|
||||
" float sd = median(msd.r, msd.g, msd.b); \n"
|
||||
" float screenPxDistance = screenPxRange() * (sd - .5); \n"
|
||||
" float alpha = clamp(screenPxDistance + .5, 0., 1.); \n"
|
||||
" if (alpha <= 0.0) discard; \n"
|
||||
" return vec4(lovrGraphicsColor.rgb, lovrGraphicsColor.a * alpha); \n"
|
||||
"}";
|
||||
|
|
Loading…
Reference in New Issue