Improved string rendering with text advance metric
This commit is contained in:
parent
d8ee46611f
commit
5ef54e16be
@ -120,13 +120,12 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
|
||||
stbtt_InitFont(&fontInfo, fontFileRead.buffer,
|
||||
stbtt_GetFontOffsetForIndex(fontFileRead.buffer, 0));
|
||||
|
||||
/* Initialise Assetmanager Font */
|
||||
Font *font = &assetManager->font;
|
||||
font->codepointRange = V2i(32, 127);
|
||||
|
||||
v2i codepointRange = font->codepointRange;
|
||||
const i32 numGlyphs = codepointRange.y - codepointRange.x;
|
||||
|
||||
i32 glyphIndex = 0;
|
||||
GlyphBitmap *glyphBitmaps =
|
||||
CAST(GlyphBitmap *) calloc(numGlyphs, sizeof(GlyphBitmap));
|
||||
v2i largestGlyphDimension = V2i(0, 0);
|
||||
@ -134,7 +133,20 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
|
||||
const f32 targetFontHeight = 64.0f;
|
||||
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 */
|
||||
i32 glyphIndex = 0;
|
||||
for (i32 codepoint = codepointRange.x; codepoint < codepointRange.y;
|
||||
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);
|
||||
|
||||
// TODO(doyle): Dimensions is used twice in font->trueSize and this
|
||||
glyphBitmaps[glyphIndex].dimensions = V2i(width, height);
|
||||
glyphBitmaps[glyphIndex].codepoint = codepoint;
|
||||
glyphBitmaps[glyphIndex++].pixels = colorBitmap;
|
||||
@ -180,7 +207,6 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
|
||||
"unoptimal\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
@ -199,12 +225,12 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
|
||||
if ((largestGlyphDimension.h & 1) == 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
|
||||
i32 glyphsPerCol = MAX_TEXTURE_SIZE / font->charSize.h;
|
||||
i32 glyphsPerCol = MAX_TEXTURE_SIZE / font->maxSize.h;
|
||||
if ((glyphsPerRow * glyphsPerCol) <= numGlyphs)
|
||||
{
|
||||
printf(
|
||||
@ -244,15 +270,22 @@ const i32 asset_loadTTFont(AssetManager *assetManager, const char *filePath)
|
||||
ASSERT(activeGlyph.codepoint < ARRAY_COUNT(fontAtlas->texRect));
|
||||
#endif
|
||||
|
||||
v2 origin = V2(CAST(f32)(glyphIndex * font->charSize.w),
|
||||
CAST(f32) row);
|
||||
v2 origin =
|
||||
V2(CAST(f32)(glyphIndex * font->maxSize.w), CAST(f32) row);
|
||||
#if 1
|
||||
fontAtlas->texRect[atlasIndex++] =
|
||||
getRect(origin, V2(CAST(f32) font->charSize.w,
|
||||
CAST(f32) font->charSize.h));
|
||||
getRect(origin, V2(CAST(f32) font->maxSize.w,
|
||||
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 */
|
||||
i32 numPixelsToPad = font->charSize.w;
|
||||
i32 numPixelsToPad = font->maxSize.w;
|
||||
if (verticalPixelsBlitted < activeGlyph.dimensions.h)
|
||||
{
|
||||
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
|
||||
* pointer by the remaining distance
|
||||
*/
|
||||
numPixelsToPad =
|
||||
font->charSize.w - activeGlyph.dimensions.w;
|
||||
numPixelsToPad = font->maxSize.w - activeGlyph.dimensions.w;
|
||||
}
|
||||
destRow += numPixelsToPad;
|
||||
}
|
||||
|
||||
/* A row of glyphs has been fully formed on the atlas */
|
||||
if (verticalPixelsBlitted++ >= font->charSize.h)
|
||||
if (verticalPixelsBlitted++ >= font->maxSize.h)
|
||||
{
|
||||
verticalPixelsBlitted = 0;
|
||||
startingGlyphIndex += glyphsPerRow;
|
||||
|
@ -28,6 +28,12 @@ void renderer_object(Renderer *renderer, v2 pos, v2 size, f32 rotate, v3 color,
|
||||
// TODO(doyle): Unimplemented
|
||||
// this->shader->uniformSetVec3f("spriteColor", color);
|
||||
|
||||
#if 1
|
||||
glBindVertexArray(renderer->vao);
|
||||
glDrawArrays(GL_TRIANGLE_STRIP, 0, renderer->numVertexesInVbo);
|
||||
glBindVertexArray(0);
|
||||
#endif
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
|
||||
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);
|
||||
glBindVertexArray(0);
|
||||
|
||||
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glCheckError();
|
||||
}
|
||||
|
@ -343,26 +343,42 @@ void worldTraveller_gameUpdateAndRender(GameState *state, const f32 dt)
|
||||
V3(0, 0, 0), worldTex);
|
||||
|
||||
Font *font = &assetManager->font;
|
||||
char *string = "hello world";
|
||||
i32 strLen = 11;
|
||||
char *string = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
||||
i32 strLen = 52;
|
||||
quadIndex = 0;
|
||||
RenderQuad *stringQuads = CAST(RenderQuad *)calloc(strLen, sizeof(RenderQuad));
|
||||
|
||||
f32 baseline = 100.0f;
|
||||
f32 xPosOnScreen = 20.0f;
|
||||
f32 yPosOnScreen = baseline;
|
||||
for (i32 i = 0; i < strLen; i++)
|
||||
{
|
||||
// NOTE(doyle): Atlas packs fonts tightly, so offset the codepoint to
|
||||
// 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);
|
||||
|
||||
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(
|
||||
&state->renderer, charRectOnScreen, charTexRect, font->tex);
|
||||
stringQuads[quadIndex++] = charQuad;
|
||||
|
@ -32,12 +32,33 @@ typedef struct TexAtlas
|
||||
v4 texRect[128];
|
||||
} 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
|
||||
{
|
||||
TexAtlas *atlas;
|
||||
Texture *tex;
|
||||
|
||||
FontMetrics metrics;
|
||||
CharMetrics *charMetrics;
|
||||
|
||||
v2i codepointRange;
|
||||
v2i charSize;
|
||||
v2i maxSize;
|
||||
|
||||
} Font;
|
||||
|
||||
// TODO(doyle): Switch to hash based lookup
|
||||
|
Loading…
Reference in New Issue
Block a user