Improved string rendering with text advance metric

This commit is contained in:
Doyle Thai 2016-06-30 00:14:07 +10:00
parent d8ee46611f
commit 5ef54e16be
4 changed files with 100 additions and 24 deletions

View File

@ -120,13 +120,12 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
stbtt_InitFont(&fontInfo, fontFileRead.buffer, stbtt_InitFont(&fontInfo, fontFileRead.buffer,
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0)); stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0));
/* Initialise Assetmanager Font */
Font *font = &assetManager->font; Font *font = &assetManager->font;
font->codepointRange = V2i(32, 127); font->codepointRange = V2i(32, 127);
v2i codepointRange = font->codepointRange; v2i codepointRange = font->codepointRange;
const i32 numGlyphs = codepointRange.y - codepointRange.x; const i32 numGlyphs = codepointRange.y - codepointRange.x;
i32 glyphIndex = 0;
GlyphBitmap *glyphBitmaps = GlyphBitmap *glyphBitmaps =
CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap)); CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap));
v2i largestGlyphDimension = V2i(0, 0); v2i largestGlyphDimension = V2i(0, 0);
@ -134,7 +133,20 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
const f32 targetFontHeight = 64.0f; const f32 targetFontHeight = 64.0f;
f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight); f32 scaleY = stbtt_ScaleForPixelHeight(&fontInfo, targetFontHeight);
i32 ascent, descent, lineGap;
stbtt_GetFontVMetrics(&fontInfo, &ascent, &descent, &lineGap);
ascent = CAST(i32)(ascent * scaleY);
descent = CAST(i32)(descent * scaleY);
lineGap = CAST(i32)(lineGap * scaleY);
font->metrics = CAST(FontMetrics){ascent, descent, lineGap};
font->charMetrics =
CAST(CharMetrics *) calloc(numGlyphs, sizeof(CharMetrics));
/* Use STB_TrueType to generate a series of bitmap characters */ /* Use STB_TrueType to generate a series of bitmap characters */
i32 glyphIndex = 0;
for (i32 codepoint = codepointRange.x; codepoint < codepointRange.y; for (i32 codepoint = codepointRange.x; codepoint < codepointRange.y;
codepoint++) codepoint++)
{ {
@ -161,7 +173,22 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
} }
} }
/* Get individual character metrics */
i32 advance, leftSideBearing;
stbtt_GetCodepointHMetrics(&fontInfo, codepoint, &advance,
&leftSideBearing);
advance = CAST(i32)(advance * scaleY);
leftSideBearing = CAST(i32)(leftSideBearing * scaleY);
font->charMetrics[glyphIndex] =
CAST(CharMetrics){advance, leftSideBearing, NULL,
V2i(xOffset, yOffset), V2i(width, height)};
/* Store bitmap into intermediate storage */
stbtt_FreeBitmap(monoBitmap, NULL); stbtt_FreeBitmap(monoBitmap, NULL);
// TODO(doyle): Dimensions is used twice in font->trueSize and this
glyphBitmaps[glyphIndex].dimensions = V2i(width, height); glyphBitmaps[glyphIndex].dimensions = V2i(width, height);
glyphBitmaps[glyphIndex].codepoint = codepoint; glyphBitmaps[glyphIndex].codepoint = codepoint;
glyphBitmaps[glyphIndex++].pixels = colorBitmap; glyphBitmaps[glyphIndex++].pixels = colorBitmap;
@ -180,7 +207,6 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
"unoptimal\n"); "unoptimal\n");
} }
#endif #endif
} }
/* /*
@ -199,12 +225,12 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
if ((largestGlyphDimension.h & 1) == 1) if ((largestGlyphDimension.h & 1) == 1)
largestGlyphDimension.h += 1; largestGlyphDimension.h += 1;
font->charSize = largestGlyphDimension; font->maxSize= largestGlyphDimension;
i32 glyphsPerRow = (MAX_TEXTURE_SIZE / font->charSize.w) + 1; i32 glyphsPerRow = (MAX_TEXTURE_SIZE / font->maxSize.w) + 1;
#ifdef WT_DEBUG #ifdef WT_DEBUG
i32 glyphsPerCol = MAX_TEXTURE_SIZE / font->charSize.h; i32 glyphsPerCol = MAX_TEXTURE_SIZE / font->maxSize.h;
if ((glyphsPerRow * glyphsPerCol) <= numGlyphs) if ((glyphsPerRow * glyphsPerCol) <= numGlyphs)
{ {
printf( printf(
@ -244,15 +270,22 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect)); ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect));
#endif #endif
v2 origin = V2(CAST(f32)(glyphIndex * font->charSize.w), v2 origin =
CAST(f32) row); V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row);
#if 1
fontAtlas->texRect[atlasIndex++] = fontAtlas->texRect[atlasIndex++] =
getRect(origin, V2(CAST(f32) font->charSize.w, getRect(origin, V2(CAST(f32) font->maxSize.w,
CAST(f32) font->charSize.h)); CAST(f32) font->maxSize.h));
#else
v2i fontSize =
font->charMetrics[activeGlyph.codepoint - 32].trueSize;
fontAtlas->texRect[atlasIndex++] =
getRect(origin, V2(CAST(f32)fontSize.x, CAST(f32)fontSize.y));
#endif
} }
/* Copy over exactly one row of pixels */ /* Copy over exactly one row of pixels */
i32 numPixelsToPad = font->charSize.w; i32 numPixelsToPad = font->maxSize.w;
if (verticalPixelsBlitted < activeGlyph.dimensions.h) if (verticalPixelsBlitted < activeGlyph.dimensions.h)
{ {
const i32 srcPitch = const i32 srcPitch =
@ -270,14 +303,13 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
* (NULL/mixes up rows), instead just advance the final bitmap * (NULL/mixes up rows), instead just advance the final bitmap
* pointer by the remaining distance * pointer by the remaining distance
*/ */
numPixelsToPad = numPixelsToPad = font->maxSize.w - activeGlyph.dimensions.w;
font->charSize.w - activeGlyph.dimensions.w;
} }
destRow += numPixelsToPad; destRow += numPixelsToPad;
} }
/* A row of glyphs has been fully formed on the atlas */ /* A row of glyphs has been fully formed on the atlas */
if (verticalPixelsBlitted++ >= font->charSize.h) if (verticalPixelsBlitted++ >= font->maxSize.h)
{ {
verticalPixelsBlitted = 0; verticalPixelsBlitted = 0;
startingGlyphIndex += glyphsPerRow; startingGlyphIndex += glyphsPerRow;

View File

@ -28,6 +28,12 @@ void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color,
// TODO(doyle): Unimplemented // TODO(doyle): Unimplemented
// this->shader->uniformSetVec3f("spriteColor", color); // this->shader->uniformSetVec3f("spriteColor", color);
#if 1
glBindVertexArray(renderer->vao);
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0);
#endif
glActiveTexture(GL_TEXTURE0); glActiveTexture(GL_TEXTURE0);
if (tex) if (tex)
@ -40,6 +46,7 @@ void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color,
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo); glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
glBindVertexArray(0); glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
glCheckError(); glCheckError();
} }

View File

@ -343,26 +343,42 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
V3(0, 0, 0), worldTex); V3(0, 0, 0), worldTex);
Font *font = &assetManager->font; Font *font = &assetManager->font;
char *string = "hello world"; char *string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
i32 strLen = 11; i32 strLen = 52;
quadIndex = 0; quadIndex = 0;
RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad)); RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad));
f32 baseline = 100.0f;
f32 xPosOnScreen = 20.0f; f32 xPosOnScreen = 20.0f;
f32 yPosOnScreen = baseline;
for (i32 i = 0; i < strLen; i++) for (i32 i = 0; i < strLen; i++)
{ {
// NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint to // NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint to
// its actual atlas index, i.e. we skip the first 31 glyphs // its actual atlas index, i.e. we skip the first 31 glyphs
i32 atlasIndex = string[i] - font->codepointRange.x; i32 codepoint = string[i];
i32 relativeIndex = codepoint - font->codepointRange.x;
CharMetrics charMetric = font->charMetrics[relativeIndex];
v4 charTexRect = font->atlas->texRect[atlasIndex]; i32 charBaseline = (font->maxSize.h - charMetric.trueSize.h);
if (codepoint == 'a')
{
yPosOnScreen = baseline - charBaseline;
}
else
{
yPosOnScreen = baseline;
}
const v4 charRectOnScreen = getRect(
V2(xPosOnScreen, yPosOnScreen),
V2(CAST(f32) font->maxSize.w, CAST(f32) font->maxSize.h));
xPosOnScreen += charMetric.advance;
/* Get texture out */
v4 charTexRect = font->atlas->texRect[relativeIndex];
renderer_flipTexCoord(&charTexRect, FALSE, TRUE); renderer_flipTexCoord(&charTexRect, FALSE, TRUE);
const v4 charRectOnScreen =
getRect(V2(xPosOnScreen, 100.0f),
V2(CAST(f32) font->charSize.w, CAST(f32) font->charSize.w));
xPosOnScreen += font->charSize.w;
RenderQuad charQuad = renderer_createQuad( RenderQuad charQuad = renderer_createQuad(
&state->renderer, charRectOnScreen, charTexRect, font->tex); &state->renderer, charRectOnScreen, charTexRect, font->tex);
stringQuads[quadIndex++] = charQuad; stringQuads[quadIndex++] = charQuad;

View File

@ -32,12 +32,33 @@ typedef struct TexAtlas
v4 texRect[128]; v4 texRect[128];
} TexAtlas; } TexAtlas;
typedef struct FontMetrics
{
i32 ascent;
i32 descent;
i32 lineGap;
} FontMetrics;
typedef struct CharMetrics
{
i32 advance;
i32 leftSideBearing;
i32 *kerning;
v2i offset;
v2i trueSize;
} CharMetrics;
typedef struct Font typedef struct Font
{ {
TexAtlas *atlas; TexAtlas *atlas;
Texture *tex; Texture *tex;
FontMetrics metrics;
CharMetrics *charMetrics;
v2i codepointRange; v2i codepointRange;
v2i charSize; v2i maxSize;
} Font; } Font;
// TODO(doyle): Switch to hash based lookup // TODO(doyle): Switch to hash based lookup