Skip to content
Open
Show file tree
Hide file tree
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
10 changes: 10 additions & 0 deletions include/c2d/font.h
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,16 @@ charWidthInfo_s* C2D_FontGetCharWidthInfo(C2D_Font font, int glyphIndex);
*/
void C2D_FontCalcGlyphPos(C2D_Font font, fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY);

/** @brief Calculate glyph position of a given Unicode codepoint. Uses a cached value when the codepoint is ASCII, flags are 0, and scales are 1.
* @param[in] font Font to read from, or NULL for system font
* @param[out] out Glyph position
* @param[in] codepoint The Unicode codepoint of the glyph
* @param[in] flags Misc flags
* @param[in] scaleX Size to scale in X
* @param[in] scaleY Size to scale in Y
*/
void C2D_FontCalcGlyphPosFromCodePoint(C2D_Font font, fontGlyphPos_s* out, u32 codepoint, u32 flags, float scaleX, float scaleY);

/** @brief Get the font info structure associated with the font
* @param[in] font Font to read from, or NULL for the system font
* @returns FINF associated with the font
Expand Down
128 changes: 96 additions & 32 deletions source/font.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#include "internal.h"
#include <c2d/font.h>

C2D_Font_s g_systemFont;

C2D_Font C2D_FontLoad(const char* filename)
{
FILE* f = fopen(filename, "rb");
Expand All @@ -18,38 +20,72 @@ static inline C2D_Font C2Di_FontAlloc(void)
return (C2D_Font)malloc(sizeof(struct C2D_Font_s));
}

static void fillSheet(C3D_Tex *tex, void *data, TGLP_s *glyphInfo)
{
tex->data = data;
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE);
tex->border = 0;
tex->lodParam = 0;
}

static C2D_Font C2Di_PostLoadFont(C2D_Font font)
{
if (!font->cfnt)
{
free(font);
font = NULL;
} else
}
else
{
fontFixPointers(font->cfnt);
if (font->cfnt != fontGetSystemFont())
{
fontFixPointers(font->cfnt);
}

TGLP_s* glyphInfo = font->cfnt->finf.tglp;
font->glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets);
font->textScale = 30.0f / glyphInfo->cellHeight;

// The way TGLP_s is set up, all of a font's texture sheets are adjacent in memory and have the same size. We can
// reinterpet the memory to describe a smaller set of much taller textures if we'd like. If we choose the right size,
// we can get all of the ASCII glyphs under a single texture, which will massively improve performance by reducing
// texture swaps within a piece of all-English text down to 0! We don't need any extra linear allocating to do this!
// Let's combine as many sheets as it takes to get to a big sheet height of 1024, which is the maximum height a
// texture can have.
font->sheetsPerBigSheet = 1024U / glyphInfo->sheetHeight;
u32 numSheetsBig = glyphInfo->nSheets / font->sheetsPerBigSheet;
u32 numSheetsSmall = glyphInfo->nSheets % font->sheetsPerBigSheet;
u32 numSheetsTotal = numSheetsBig + numSheetsSmall;
font->numSheetsCombined = glyphInfo->nSheets - numSheetsSmall;

font->glyphSheets = malloc(sizeof(C3D_Tex)*numSheetsTotal);
if (!font->glyphSheets)
{
C2D_FontFree(font);
return NULL;
}

int i;
for (i = 0; i < glyphInfo->nSheets; i++)
memset(font->glyphSheets, 0, sizeof(sizeof(C3D_Tex)*numSheetsTotal));
for (u32 i = 0; i < numSheetsBig; i++)
{
C3D_Tex* tex = &font->glyphSheets[i];
tex->data = &glyphInfo->sheetData[glyphInfo->sheetSize*i];
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER);
tex->border = 0;
tex->lodParam = 0;
fillSheet(tex, fontGetGlyphSheetTex(font->cfnt, i * font->sheetsPerBigSheet), glyphInfo);
tex->height = (uint16_t) (tex->height * font->sheetsPerBigSheet);
tex->size = tex->size * font->sheetsPerBigSheet;
}

for (u32 i = 0; i < numSheetsSmall; i++)
{
fillSheet(&font->glyphSheets[numSheetsBig + i], fontGetGlyphSheetTex(font->cfnt, numSheetsBig * font->sheetsPerBigSheet + i), glyphInfo);
}

for (u32 i = 0; i < NUM_ASCII_CHARACTERS; i++)
{
// This will readjust glyph UVs to account for being a part of the combined texture.
C2D_FontCalcGlyphPos(NULL, &font->asciiCache[i], fontGlyphIndexFromCodePoint(font->cfnt, i), 0, 1.0, 1.0);
}
}
return font;
Expand Down Expand Up @@ -151,6 +187,13 @@ static C2D_Font C2Di_FontLoadFromArchive(u64 tid, const char* path)
return C2Di_PostLoadFont(font);
}

C2D_Font C2Di_LoadSystemFont(void)
{
g_systemFont.cfnt = fontGetSystemFont();
C2Di_PostLoadFont(&g_systemFont);
return &g_systemFont;
}

static unsigned C2Di_RegionToFontIndex(CFG_Region region)
{
switch (region)
Expand Down Expand Up @@ -196,7 +239,7 @@ C2D_Font C2D_FontLoadSystem(CFG_Region region)

void C2D_FontFree(C2D_Font font)
{
if (font)
if (font && font != &g_systemFont)
{
if (font->cfnt)
linearFree(font->cfnt);
Expand All @@ -206,7 +249,7 @@ void C2D_FontFree(C2D_Font font)

void C2D_FontSetFilter(C2D_Font font, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TEXTURE_FILTER_PARAM minFilter)
{
if (!font)
if (!font || font == &g_systemFont)
return;

TGLP_s* glyphInfo = font->cfnt->finf.tglp;
Expand All @@ -221,32 +264,53 @@ void C2D_FontSetFilter(C2D_Font font, GPU_TEXTURE_FILTER_PARAM magFilter, GPU_TE

int C2D_FontGlyphIndexFromCodePoint(C2D_Font font, u32 codepoint)
{
if (!font)
return fontGlyphIndexFromCodePoint(fontGetSystemFont(), codepoint);
else
return fontGlyphIndexFromCodePoint(font->cfnt, codepoint);
if (!font) font = &g_systemFont;
return fontGlyphIndexFromCodePoint(font->cfnt, codepoint);
}

charWidthInfo_s* C2D_FontGetCharWidthInfo(C2D_Font font, int glyphIndex)
{
if (!font)
return fontGetCharWidthInfo(fontGetSystemFont(), glyphIndex);
else
return fontGetCharWidthInfo(font->cfnt, glyphIndex);
if (!font) font = &g_systemFont;
return fontGetCharWidthInfo(font->cfnt, glyphIndex);
}

void C2D_FontCalcGlyphPos(C2D_Font font, fontGlyphPos_s* out, int glyphIndex, u32 flags, float scaleX, float scaleY)
{
if (!font)
fontCalcGlyphPos(out, fontGetSystemFont(), glyphIndex, flags, scaleX, scaleY);
if (!font) font = &g_systemFont;
fontCalcGlyphPos(out, font->cfnt, glyphIndex, flags, scaleX, scaleY);

if (out->sheetIndex < font->numSheetsCombined)
{
u32 indexWithinBigSheet = out->sheetIndex % font->sheetsPerBigSheet;
out->sheetIndex /= font->sheetsPerBigSheet;

// Readjust glyph UVs to account for being a part of the combined texture.
out->texcoord.top = (out->texcoord.top + (font->sheetsPerBigSheet - indexWithinBigSheet - 1)) / (float) font->sheetsPerBigSheet;
out->texcoord.bottom = (out->texcoord.bottom + (font->sheetsPerBigSheet - indexWithinBigSheet - 1)) / (float) font->sheetsPerBigSheet;
}
else
{
out->sheetIndex = out->sheetIndex - font->numSheetsCombined + font->numSheetsCombined / font->sheetsPerBigSheet;
}
}

void C2D_FontCalcGlyphPosFromCodePoint(C2D_Font font, fontGlyphPos_s* out, u32 codepoint, u32 flags, float scaleX, float scaleY)
{
if (!font) font = &g_systemFont;

// Building glyph positions is pretty expensive, but we could just store the results for plain ASCII.
if (codepoint < NUM_ASCII_CHARACTERS && flags == 0 && scaleX == 1 && scaleY == 1)
{
*out = font->asciiCache[codepoint];
}
else
fontCalcGlyphPos(out, font->cfnt, glyphIndex, flags, scaleX, scaleY);
{
C2D_FontCalcGlyphPos(font, out, C2D_FontGlyphIndexFromCodePoint(font, codepoint), 0, 1.0f, 1.0f);
}
}

FINF_s* C2D_FontGetInfo(C2D_Font font)
{
if (!font)
return fontGetInfo(NULL);
else
return fontGetInfo(font->cfnt);
if (!font) font = &g_systemFont;
return fontGetInfo(font->cfnt);
}
13 changes: 11 additions & 2 deletions source/internal.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
#pragma once
#include <c2d/base.h>
#include <c2d/font.h>
#include <3ds/font.h>

typedef struct
{
Expand Down Expand Up @@ -72,12 +74,16 @@ enum
C2DiF_DirtyAny = C2DiF_DirtyProj | C2DiF_DirtyMdlv | C2DiF_DirtyTex | C2DiF_DirtyMode | C2DiF_DirtyFade,
};

struct C2D_Font_s
#define NUM_ASCII_CHARACTERS 128
typedef struct C2D_Font_s
{
CFNT_s* cfnt;
C3D_Tex* glyphSheets;
float textScale;
};
u32 numSheetsCombined;
u32 sheetsPerBigSheet;
fontGlyphPos_s asciiCache[NUM_ASCII_CHARACTERS];
} C2D_Font_s;

static inline C2Di_Context* C2Di_GetContext(void)
{
Expand Down Expand Up @@ -117,3 +123,6 @@ void C2Di_AppendQuad(void);
void C2Di_AppendVtx(float x, float y, float z, float u, float v, float ptx, float pty, u32 color);
void C2Di_FlushVtxBuf(void);
void C2Di_Update(void);

extern C2D_Font_s g_systemFont;
C2D_Font C2Di_LoadSystemFont(void);
70 changes: 17 additions & 53 deletions source/text.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@
#include <stdlib.h>
#include <stdarg.h>

static C3D_Tex* s_glyphSheets;
static float s_textScale;

typedef struct C2Di_Glyph_s
{
u32 lineNo;
Expand Down Expand Up @@ -60,35 +57,15 @@ static int C2Di_GlyphComp(const void* _g1, const void* _g2)
static void C2Di_TextEnsureLoad(void)
{
// Skip if already loaded
if (s_glyphSheets)
if (g_systemFont.glyphSheets)
return;

// Ensure the shared system font is mapped
if (R_FAILED(fontEnsureMapped()))
svcBreak(USERBREAK_PANIC);

// Load the glyph texture sheets
CFNT_s* font = fontGetSystemFont();
TGLP_s* glyphInfo = fontGetGlyphInfo(font);
s_glyphSheets = malloc(sizeof(C3D_Tex)*glyphInfo->nSheets);
s_textScale = 30.0f / glyphInfo->cellHeight;
if (!s_glyphSheets)
if (!C2Di_LoadSystemFont())
svcBreak(USERBREAK_PANIC);

int i;
for (i = 0; i < glyphInfo->nSheets; i ++)
{
C3D_Tex* tex = &s_glyphSheets[i];
tex->data = fontGetGlyphSheetTex(font, i);
tex->fmt = glyphInfo->sheetFmt;
tex->size = glyphInfo->sheetSize;
tex->width = glyphInfo->sheetWidth;
tex->height = glyphInfo->sheetHeight;
tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR)
| GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_BORDER) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_BORDER);
tex->border = 0;
tex->lodParam = 0;
}
}

C2D_TextBuf C2D_TextBufNew(size_t maxGlyphs)
Expand Down Expand Up @@ -139,6 +116,8 @@ const char* C2D_TextParseLine(C2D_Text* text, C2D_TextBuf buf, const char* str,

const char* C2D_TextFontParseLine(C2D_Text* text, C2D_Font font, C2D_TextBuf buf, const char* str, u32 lineNo)
{
if (!font) font = &g_systemFont;

const uint8_t* p = (const uint8_t*)str;
text->font = font;
text->buf = buf;
Expand All @@ -161,14 +140,11 @@ const char* C2D_TextFontParseLine(C2D_Text* text, C2D_Font font, C2D_TextBuf buf
p += units;

fontGlyphPos_s glyphData;
C2D_FontCalcGlyphPos(font, &glyphData, C2D_FontGlyphIndexFromCodePoint(font, code), 0, 1.0f, 1.0f);
C2D_FontCalcGlyphPosFromCodePoint(font, &glyphData, code, 0, 1.0f, 1.0f);
if (glyphData.width > 0.0f)
{
C2Di_Glyph* glyph = &buf->glyphs[buf->glyphCount++];
if (font)
glyph->sheet = &font->glyphSheets[glyphData.sheetIndex];
else
glyph->sheet = &s_glyphSheets[glyphData.sheetIndex];
glyph->sheet = &font->glyphSheets[glyphData.sheetIndex];
glyph->xPos = text->width + glyphData.xOffset;
glyph->lineNo = lineNo;
glyph->wordNo = wordNum;
Expand All @@ -192,7 +168,7 @@ const char* C2D_TextFontParseLine(C2D_Text* text, C2D_Font font, C2D_TextBuf buf
wordNum++;

text->end = buf->glyphCount;
text->width *= font ? font->textScale : s_textScale;
text->width *= font->textScale;
text->lines = 1;
text->words = wordNum;
return (const char*)p;
Expand All @@ -205,6 +181,8 @@ const char* C2D_TextParse(C2D_Text* text, C2D_TextBuf buf, const char* str)

const char* C2D_TextFontParse(C2D_Text* text, C2D_Font font, C2D_TextBuf buf, const char* str)
{
if (!font) font = &g_systemFont;

text->font = font;
text->buf = buf;
text->begin = buf->glyphCount;
Expand Down Expand Up @@ -240,10 +218,8 @@ void C2D_TextGetDimensions(const C2D_Text* text, float scaleX, float scaleY, flo
*outWidth = scaleX*text->width;
if (outHeight)
{
if (text->font)
*outHeight = ceilf(scaleY*text->font->textScale*text->font->cfnt->finf.lineFeed)*text->lines;
else
*outHeight = ceilf(scaleY*s_textScale*fontGetInfo(fontGetSystemFont())->lineFeed)*text->lines;
C2D_Font font = text->font ? text->font : &g_systemFont;
*outHeight = ceilf(scaleY*font->textScale*font->cfnt->finf.lineFeed)*text->lines;
}
}

Expand Down Expand Up @@ -321,24 +297,15 @@ void C2D_DrawText(const C2D_Text* text, u32 flags, float x, float y, float z, fl
C2Di_Glyph* begin = &text->buf->glyphs[text->begin];
C2Di_Glyph* end = &text->buf->glyphs[text->end];
C2Di_Glyph* cur;
CFNT_s* systemFont = fontGetSystemFont();

float glyphZ = z;
float glyphH;
float dispY;
if (text->font)
{
scaleX *= text->font->textScale;
scaleY *= text->font->textScale;
glyphH = scaleY*text->font->cfnt->finf.tglp->cellHeight;
dispY = ceilf(scaleY*text->font->cfnt->finf.lineFeed);
} else
{
scaleX *= s_textScale;
scaleY *= s_textScale;
glyphH = scaleY*fontGetGlyphInfo(systemFont)->cellHeight;
dispY = ceilf(scaleY*fontGetInfo(systemFont)->lineFeed);
}
C2D_Font font = text->font ? text->font : &g_systemFont;
scaleX *= font->textScale;
scaleY *= font->textScale;
glyphH = scaleY*font->cfnt->finf.tglp->cellHeight;
dispY = ceilf(scaleY*font->cfnt->finf.lineFeed);
u32 color = 0xFF000000;
float maxWidth = scaleX*text->width;

Expand All @@ -347,10 +314,7 @@ void C2D_DrawText(const C2D_Text* text, u32 flags, float x, float y, float z, fl

if (flags & C2D_AtBaseline)
{
if (text->font)
y -= scaleY*text->font->cfnt->finf.tglp->baselinePos;
else
y -= scaleY*fontGetGlyphInfo(systemFont)->baselinePos;
y -= scaleY*font->cfnt->finf.tglp->baselinePos;
}
if (flags & C2D_WithColor)
color = va_arg(va, u32);
Expand Down