Slightly modified UTF-8 handling. Based on http://man7.org/linux/man-pages/man7/utf-8.7.html.

This commit is contained in:
Steve 2019-11-06 19:18:03 +00:00
parent cbd7425a31
commit 98274ffa34
1 changed files with 129 additions and 108 deletions

View File

@ -40,11 +40,11 @@ void initFonts(void)
{ {
memset(&fontHead, 0, sizeof(Font)); memset(&fontHead, 0, sizeof(Font));
fontTail = &fontHead; fontTail = &fontHead;
initFont("roboto", getFileLocation("data/fonts/Roboto-Medium.ttf")); initFont("roboto", getFileLocation("data/fonts/Roboto-Medium.ttf"));
initFont("khosrau", getFileLocation("data/fonts/Khosrau.ttf")); initFont("khosrau", getFileLocation("data/fonts/Khosrau.ttf"));
useFont("roboto"); useFont("roboto");
} }
@ -58,59 +58,59 @@ static void initFont(char *name, char *filename)
Glyph *g; Glyph *g;
int i; int i;
SDL_Color white = {255, 255, 255, 255}; SDL_Color white = {255, 255, 255, 255};
f = malloc(sizeof(Font)); f = malloc(sizeof(Font));
memset(f, 0, sizeof(Font)); memset(f, 0, sizeof(Font));
font = TTF_OpenFont(filename, FONT_SIZE); font = TTF_OpenFont(filename, FONT_SIZE);
initChars(f); initChars(f);
surface = SDL_CreateRGBSurface(0, FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE, 32, 0, 0, 0, 0xff); surface = SDL_CreateRGBSurface(0, FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE, 32, 0, 0, 0, 0xff);
SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGBA(surface->format, 0, 0, 0, 0)); SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGBA(surface->format, 0, 0, 0, 0));
dest.x = dest.y = 0; dest.x = dest.y = 0;
for (i = 0 ; i < NUM_GLYPH_BUCKETS ; i++) for (i = 0 ; i < NUM_GLYPH_BUCKETS ; i++)
{ {
for (g = f->glyphHead[i].next ; g != NULL ; g = g->next) for (g = f->glyphHead[i].next ; g != NULL ; g = g->next)
{ {
text = TTF_RenderUTF8_Blended(font, g->character, white); text = TTF_RenderUTF8_Blended(font, g->character, white);
TTF_SizeText(font, g->character, &dest.w, &dest.h); TTF_SizeText(font, g->character, &dest.w, &dest.h);
if (dest.x + dest.w >= FONT_TEXTURE_SIZE) if (dest.x + dest.w >= FONT_TEXTURE_SIZE)
{ {
dest.x = 0; dest.x = 0;
dest.y += dest.h + 1; dest.y += dest.h + 1;
if (dest.y + dest.h >= FONT_TEXTURE_SIZE) if (dest.y + dest.h >= FONT_TEXTURE_SIZE)
{ {
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL, "Out of glyph space in %dx%d font atlas texture map.", FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL, "Out of glyph space in %dx%d font atlas texture map.", FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE);
exit(1); exit(1);
} }
} }
SDL_BlitSurface(text, NULL, surface, &dest); SDL_BlitSurface(text, NULL, surface, &dest);
g->rect = dest; g->rect = dest;
SDL_FreeSurface(text); SDL_FreeSurface(text);
dest.x += dest.w; dest.x += dest.w;
} }
} }
TTF_CloseFont(font); TTF_CloseFont(font);
texture = toTexture(surface, 1); texture = toTexture(surface, 1);
f->texture = texture; f->texture = texture;
strcpy(f->name, name); strcpy(f->name, name);
fontTail->next = f; fontTail->next = f;
fontTail = f; fontTail = f;
} }
@ -120,17 +120,17 @@ static void initChars(Font *f)
char *characters, *character; char *characters, *character;
Glyph *g, *glyphTail; Glyph *g, *glyphTail;
int i, bucket; int i, bucket;
characters = readFile("data/locale/characters.dat"); characters = readFile("data/locale/characters.dat");
i = 0; i = 0;
character = nextCharacter(characters, &i); character = nextCharacter(characters, &i);
while (character) while (character)
{ {
bucket = hashcode(character) % NUM_GLYPH_BUCKETS; bucket = hashcode(character) % NUM_GLYPH_BUCKETS;
glyphTail = &f->glyphHead[bucket]; glyphTail = &f->glyphHead[bucket];
/* horrible bit to look for the tail */ /* horrible bit to look for the tail */
@ -138,35 +138,35 @@ static void initChars(Font *f)
{ {
glyphTail = glyphTail->next; glyphTail = glyphTail->next;
} }
g = malloc(sizeof(Glyph)); g = malloc(sizeof(Glyph));
memset(g, 0, sizeof(Glyph)); memset(g, 0, sizeof(Glyph));
glyphTail->next = g; glyphTail->next = g;
glyphTail = g; glyphTail = g;
STRNCPY(g->character, character, MAX_NAME_LENGTH); STRNCPY(g->character, character, MAX_NAME_LENGTH);
character = nextCharacter(characters, &i); character = nextCharacter(characters, &i);
} }
free(characters); free(characters);
} }
void drawText(int x, int y, int size, int align, SDL_Color color, const char *format, ...) void drawText(int x, int y, int size, int align, SDL_Color color, const char *format, ...)
{ {
va_list args; va_list args;
if (activeFont) if (activeFont)
{ {
SDL_SetTextureColorMod(activeFont->texture, color.r, color.g, color.b); SDL_SetTextureColorMod(activeFont->texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(activeFont->texture, color.a); SDL_SetTextureAlphaMod(activeFont->texture, color.a);
memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer)); memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer));
va_start(args, format); va_start(args, format);
vsprintf(drawTextBuffer, format, args); vsprintf(drawTextBuffer, format, args);
va_end(args); va_end(args);
if (app.textWidth == 0) if (app.textWidth == 0)
{ {
drawTextLine(x, y, size, align, drawTextBuffer); drawTextLine(x, y, size, align, drawTextBuffer);
@ -182,43 +182,43 @@ static void drawTextLines(int x, int y, int size, int align)
{ {
char line[MAX_LINE_LENGTH], token[MAX_WORD_LENGTH]; char line[MAX_LINE_LENGTH], token[MAX_WORD_LENGTH];
int i, n, w, h, currentWidth, len; int i, n, w, h, currentWidth, len;
memset(&line, '\0', sizeof(line)); memset(&line, '\0', sizeof(line));
memset(&token, '\0', sizeof(token)); memset(&token, '\0', sizeof(token));
len = strlen(drawTextBuffer); len = strlen(drawTextBuffer);
n = currentWidth = 0; n = currentWidth = 0;
for (i = 0 ; i < len ; i++) for (i = 0 ; i < len ; i++)
{ {
token[n++] = drawTextBuffer[i]; token[n++] = drawTextBuffer[i];
if (drawTextBuffer[i] == ' ' || i == len - 1) if (drawTextBuffer[i] == ' ' || i == len - 1)
{ {
calcTextDimensions(token, size, &w, &h); calcTextDimensions(token, size, &w, &h);
if (currentWidth + w > app.textWidth) if (currentWidth + w > app.textWidth)
{ {
drawTextLine(x, y, size, align, line); drawTextLine(x, y, size, align, line);
currentWidth = 0; currentWidth = 0;
y += h; y += h;
memset(&line, '\0', sizeof(line)); memset(&line, '\0', sizeof(line));
} }
strcat(line, token); strcat(line, token);
n = 0; n = 0;
memset(&token, '\0', sizeof(token)); memset(&token, '\0', sizeof(token));
currentWidth += w; currentWidth += w;
} }
} }
drawTextLine(x, y, size, align, line); drawTextLine(x, y, size, align, line);
} }
@ -226,17 +226,17 @@ static void drawTextLine(int x, int y, int size, int align, const char *line)
{ {
int i, startX, n, w, h; int i, startX, n, w, h;
char word[MAX_WORD_LENGTH]; char word[MAX_WORD_LENGTH];
scale = size / (FONT_SIZE * 1.0f); scale = size / (FONT_SIZE * 1.0f);
startX = x; startX = x;
memset(word, 0, MAX_WORD_LENGTH); memset(word, 0, MAX_WORD_LENGTH);
n = 0; n = 0;
calcTextDimensions(line, size, &w, &h); calcTextDimensions(line, size, &w, &h);
if (align == TA_RIGHT) if (align == TA_RIGHT)
{ {
x -= w; x -= w;
@ -245,21 +245,21 @@ static void drawTextLine(int x, int y, int size, int align, const char *line)
{ {
x -= (w / 2); x -= (w / 2);
} }
for (i = 0 ; i < strlen(line) ; i++) for (i = 0 ; i < strlen(line) ; i++)
{ {
word[n++] = line[i]; word[n++] = line[i];
if (line[i] == ' ') if (line[i] == ' ')
{ {
drawWord(word, &x, &y, startX); drawWord(word, &x, &y, startX);
memset(word, 0, MAX_WORD_LENGTH); memset(word, 0, MAX_WORD_LENGTH);
n = 0; n = 0;
} }
} }
drawWord(word, &x, &y, startX); drawWord(word, &x, &y, startX);
} }
@ -269,24 +269,24 @@ static void drawWord(char *word, int *x, int *y, int startX)
char *character; char *character;
SDL_Rect dest; SDL_Rect dest;
Glyph *g; Glyph *g;
i = 0; i = 0;
character = nextCharacter(word, &i); character = nextCharacter(word, &i);
while (character) while (character)
{ {
g = findGlyph(character); g = findGlyph(character);
dest.x = *x; dest.x = *x;
dest.y = *y; dest.y = *y;
dest.w = g->rect.w * scale; dest.w = g->rect.w * scale;
dest.h = g->rect.h * scale; dest.h = g->rect.h * scale;
SDL_RenderCopy(app.renderer, activeFont->texture, &g->rect, &dest); SDL_RenderCopy(app.renderer, activeFont->texture, &g->rect, &dest);
*x += g->rect.w * scale; *x += g->rect.w * scale;
character = nextCharacter(word, &i); character = nextCharacter(word, &i);
} }
} }
@ -295,9 +295,9 @@ static Glyph *findGlyph(char *c)
{ {
Glyph *g; Glyph *g;
int bucket; int bucket;
bucket = hashcode(c) % NUM_GLYPH_BUCKETS; bucket = hashcode(c) % NUM_GLYPH_BUCKETS;
for (g = activeFont->glyphHead[bucket].next ; g != NULL ; g = g->next) for (g = activeFont->glyphHead[bucket].next ; g != NULL ; g = g->next)
{ {
if (strcmp(g->character, c) == 0) if (strcmp(g->character, c) == 0)
@ -305,17 +305,17 @@ static Glyph *findGlyph(char *c)
return g; return g;
} }
} }
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL, "Couldn't find glyph for '%s'", c); SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL, "Couldn't find glyph for '%s'", c);
exit(1); exit(1);
return NULL; return NULL;
} }
void useFont(char *name) void useFont(char *name)
{ {
Font *f; Font *f;
for (f = fontHead.next ; f != NULL ; f = f->next) for (f = fontHead.next ; f != NULL ; f = f->next)
{ {
if (strcmp(f->name, name) == 0) if (strcmp(f->name, name) == 0)
@ -332,23 +332,23 @@ void calcTextDimensions(const char *text, int size, int *w, int *h)
int i; int i;
char *character; char *character;
Glyph *g; Glyph *g;
scale = size / (FONT_SIZE * 1.0f); scale = size / (FONT_SIZE * 1.0f);
*w = 0; *w = 0;
*h = 0; *h = 0;
i = 0; i = 0;
character = nextCharacter(text, &i); character = nextCharacter(text, &i);
while (character) while (character)
{ {
g = findGlyph(character); g = findGlyph(character);
*w += g->rect.w * scale; *w += g->rect.w * scale;
*h = MAX(g->rect.h * scale, *h); *h = MAX(g->rect.h * scale, *h);
character = nextCharacter(text, &i); character = nextCharacter(text, &i);
} }
} }
@ -357,66 +357,87 @@ int getWrappedTextHeight(char *text, int size)
{ {
char word[MAX_WORD_LENGTH]; char word[MAX_WORD_LENGTH];
int i, y, n, w, h, currentWidth, len; int i, y, n, w, h, currentWidth, len;
STRNCPY(drawTextBuffer, text, MAX_LINE_LENGTH); STRNCPY(drawTextBuffer, text, MAX_LINE_LENGTH);
n = 0; n = 0;
y = 0; y = 0;
h = 0; h = 0;
currentWidth = 0; currentWidth = 0;
len = strlen(drawTextBuffer); len = strlen(drawTextBuffer);
memset(word, 0, MAX_WORD_LENGTH); memset(word, 0, MAX_WORD_LENGTH);
for (i = 0 ; i < len ; i++) for (i = 0 ; i < len ; i++)
{ {
word[n++] = drawTextBuffer[i]; word[n++] = drawTextBuffer[i];
if (drawTextBuffer[i] == ' ' || i == len - 1) if (drawTextBuffer[i] == ' ' || i == len - 1)
{ {
calcTextDimensions(word, size, &w, &h); calcTextDimensions(word, size, &w, &h);
if (currentWidth + w > app.textWidth) if (currentWidth + w > app.textWidth)
{ {
currentWidth = 0; currentWidth = 0;
y += h; y += h;
} }
currentWidth += w; currentWidth += w;
memset(word, 0, MAX_WORD_LENGTH); memset(word, 0, MAX_WORD_LENGTH);
n = 0; n = 0;
} }
} }
return y + h; return y + h;
} }
static char *nextCharacter(const char *str, int *i) static char *nextCharacter(const char *str, int *i)
{ {
static char character[MAX_NAME_LENGTH]; static char character[MAX_NAME_LENGTH];
unsigned char bit; unsigned char bit;
int n; int n, numBits;
memset(character, '\0', MAX_NAME_LENGTH); memset(character, '\0', MAX_NAME_LENGTH);
n = 0; bit = (unsigned char)str[*i];
while (1) numBits = 0;
if (bit == '\0')
{ {
bit = (unsigned char)str[*i]; return NULL;
if ((bit >= ' ' && bit <= '~') || bit >= 0xC0 || bit == '\0')
{
if (n > 0)
{
return character[0] != '\0' ? character : NULL;
}
}
character[n++] = str[*i];
*i = *i + 1;
} }
else if (bit <= 0x0000007F)
{
numBits = 1;
}
else if (bit <= 0x000007FF)
{
numBits = 2;
}
else if (bit <= 0x0000FFFF)
{
numBits = 3;
}
else if (bit <= 0x001FFFFF)
{
numBits = 4;
}
else if (bit <= 0x03FFFFFF)
{
numBits = 5;
}
else if (bit <= 0x7FFFFFFF)
{
numBits = 6;
}
for (n = 0 ; n < numBits ; n++)
{
character[n] = str[(*i)++];
}
return character;
} }