Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 131 additions & 14 deletions source/text.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
#include "text.h"

#define NUM_ASCII_CHARS 128

static C3D_Tex* s_glyphSheets;
static float s_textScale;
static int s_textLang = CFG_LANGUAGE_EN;
static charWidthInfo_s* s_asciiCacheCharWidth[NUM_ASCII_CHARS];
// @Note: Could use s_asciiCacheCharWidth to reimplement fontCalcGlyphPos, but it would cache slightly less computations.
static fontGlyphPos_s s_asciiCacheGlyphPos[NUM_ASCII_CHARS];

void textInit(void)
{
Expand All @@ -28,6 +33,15 @@ void textInit(void)
tex->lodParam = 0;
}

// Cache up front the results of fontGetCharWidthInfo and fontCalcGlyphPos for ASCII characters, since these functions
// can potentially take a long time.
for (uint32_t i = 0; i < NUM_ASCII_CHARS; i++)
{
int glyphIdx = fontGlyphIndexFromCodePoint(NULL, i);
s_asciiCacheCharWidth[i] = fontGetCharWidthInfo(NULL, glyphIdx);
fontCalcGlyphPos(&s_asciiCacheGlyphPos[i], NULL, glyphIdx, GLYPH_POS_CALC_VTXCOORD, 1, 1);
}

Result res = cfguInit();
if (R_SUCCEEDED(res))
{
Expand Down Expand Up @@ -71,6 +85,55 @@ static inline float maxf(float a, float b)
return a > b ? a : b;
}

charWidthInfo_s *textGetCharWidthFromCodePoint(uint32_t code)
{
charWidthInfo_s *result;

if (code < NUM_ASCII_CHARS)
{
result = s_asciiCacheCharWidth[code];
}
else
{
int glyphIdx = fontGlyphIndexFromCodePoint(NULL, code);
result = fontGetCharWidthInfo(NULL, glyphIdx);
}

return result;
}

fontGlyphPos_s textGetGlyphPosFromCodePoint(uint32_t code, uint32_t flags, float scaleX, float scaleY)
{
fontGlyphPos_s result;

if (code < NUM_ASCII_CHARS)
{
result = s_asciiCacheGlyphPos[code];

if ((flags & GLYPH_POS_AT_BASELINE))
{
float baselineOffset = fontGetSystemFont()->finf.tglp->baselinePos;
result.vtxcoord.top -= baselineOffset;
result.vtxcoord.bottom -= baselineOffset;
}

result.xOffset *= scaleX;
result.xAdvance *= scaleX;
result.width *= scaleX;
result.vtxcoord.left *= scaleX;
result.vtxcoord.right *= scaleX;
result.vtxcoord.top *= scaleY;
result.vtxcoord.bottom *= scaleY;
}
else
{
int glyphIdx = fontGlyphIndexFromCodePoint(NULL, code);
fontCalcGlyphPos(&result, NULL, glyphIdx, flags, scaleX, scaleY);
}

return result;
}

float textCalcWidth(const char* text)
{
float width = 0.0f;
Expand All @@ -95,14 +158,33 @@ float textCalcWidth(const char* text)

if (code > 0)
{
int glyphIdx = fontGlyphIndexFromCodePoint(NULL, code);
charWidthInfo_s* cwi = fontGetCharWidthInfo(NULL, glyphIdx);
charWidthInfo_s* cwi = textGetCharWidthFromCodePoint(code);
width += cwi->charWidth;
}
} while (code > 0);
return s_textScale*maxf(width, maxWidth);
}

typedef struct SortedGlyph
{
int indexBuf;
int indexSheet;
float x, y;
} SortedGlyph;

static int cmpGlyphSort(const void *p1, const void *p2)
{
const SortedGlyph lhs = *(SortedGlyph*)p1;
const SortedGlyph rhs = *(SortedGlyph*)p2;

return lhs.indexSheet - rhs.indexSheet;
}

#define MAX_GLYPHS_PER_STRING 256
static fontGlyphPos_s s_bufGlyph[MAX_GLYPHS_PER_STRING];
static SortedGlyph s_bufSort[MAX_GLYPHS_PER_STRING];
static int s_glyphCount;

void textDraw(float x, float y, float scaleX, float scaleY, bool baseline, const char* text)
{
ssize_t units;
Expand All @@ -113,6 +195,12 @@ void textDraw(float x, float y, float scaleX, float scaleY, bool baseline, const
u32 flags = GLYPH_POS_CALC_VTXCOORD | (baseline ? GLYPH_POS_AT_BASELINE : 0);
scaleX *= s_textScale;
scaleY *= s_textScale;

int vertexCount = 0;
C3D_Tex *sheetCur = NULL;

s_glyphCount = 0;

do
{
if (!*p) break;
Expand All @@ -127,22 +215,51 @@ void textDraw(float x, float y, float scaleX, float scaleY, bool baseline, const
}
else if (code > 0)
{
int glyphIdx = fontGlyphIndexFromCodePoint(NULL, code);
fontGlyphPos_s data;
fontCalcGlyphPos(&data, NULL, glyphIdx, flags, scaleX, scaleY);
s_bufGlyph[s_glyphCount] = textGetGlyphPosFromCodePoint(code, flags, scaleX, scaleY);
s_bufSort[s_glyphCount] = (SortedGlyph) { s_glyphCount, s_bufGlyph[s_glyphCount].sheetIndex, x, y };
x += s_bufGlyph[s_glyphCount].xAdvance;
s_glyphCount++;
}
} while (code > 0 && s_glyphCount < MAX_GLYPHS_PER_STRING); // If the string is too long, we'll truncate it.

// Draw the glyph
drawingSetTex(&s_glyphSheets[data.sheetIndex]);
drawingAddVertex(x+data.vtxcoord.left, y+data.vtxcoord.bottom, data.texcoord.left, data.texcoord.bottom);
drawingAddVertex(x+data.vtxcoord.right, y+data.vtxcoord.bottom, data.texcoord.right, data.texcoord.bottom);
drawingAddVertex(x+data.vtxcoord.left, y+data.vtxcoord.top, data.texcoord.left, data.texcoord.top);
drawingAddVertex(x+data.vtxcoord.right, y+data.vtxcoord.top, data.texcoord.right, data.texcoord.top);
drawingSubmitPrim(GPU_TRIANGLE_STRIP, 4);
// For performance reasons, we want to try to batch up as many glyphs per draw call as we can. To do this, all the
// glyphs must be in the same texture. But, the system font is split up into many textures that contain 5 glyphs
// each. If we were to batch glyphs naively, we'd typically swapping textures constantly within a piece of text, and
// that's devastating for performance. The best we can do is sort by glyph texture and batch that way.
qsort(s_bufSort, s_glyphCount, sizeof(s_bufSort[0]), cmpGlyphSort);

x += data.xAdvance;
for (int i = 0; i < s_glyphCount; i++)
{
SortedGlyph sorted = s_bufSort[i];
fontGlyphPos_s *data = &s_bufGlyph[sorted.indexBuf];
C3D_Tex *sheetGlyph = &s_glyphSheets[sorted.indexSheet];
if (sheetCur != sheetGlyph)
{
if (vertexCount > 0)
{
drawingSubmitPrim(GPU_TRIANGLES, vertexCount);
vertexCount = 0;
}

sheetCur = sheetGlyph;
drawingSetTex(sheetCur);
}
} while (code > 0);

// Draw the glyph
drawingAddVertex(sorted.x+data->vtxcoord.left, sorted.y+data->vtxcoord.bottom, data->texcoord.left, data->texcoord.bottom);
drawingAddVertex(sorted.x+data->vtxcoord.right, sorted.y+data->vtxcoord.bottom, data->texcoord.right, data->texcoord.bottom);
drawingAddVertex(sorted.x+data->vtxcoord.left, sorted.y+data->vtxcoord.top, data->texcoord.left, data->texcoord.top);
drawingAddVertex(sorted.x+data->vtxcoord.left, sorted.y+data->vtxcoord.top, data->texcoord.left, data->texcoord.top);
drawingAddVertex(sorted.x+data->vtxcoord.right, sorted.y+data->vtxcoord.bottom, data->texcoord.right, data->texcoord.bottom);
drawingAddVertex(sorted.x+data->vtxcoord.right, sorted.y+data->vtxcoord.top, data->texcoord.right, data->texcoord.top);

vertexCount += 6;
}

if (vertexCount > 0)
{
drawingSubmitPrim(GPU_TRIANGLES, vertexCount);
}
}

void textDrawInBox(const char* text, int orientation, float scaleX, float scaleY, float baseline, float left, float right)
Expand Down