blobwarsAttrition/src/system/text.c

441 lines
8.4 KiB
C
Raw Normal View History

2018-01-21 10:31:38 +01:00
/*
2019-06-02 17:13:34 +02:00
Copyright (C) 2018-2019 Parallel Realities
2018-01-21 10:31:38 +01:00
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "text.h"
2018-12-22 23:46:38 +01:00
static void initFont(char *name, char *filename);
static void drawWord(char *word, int *x, int *y, int startX);
2018-12-23 16:32:33 +01:00
static void drawTextLines(int x, int y, int size, int align);
static void drawTextLine(int x, int y, int size, int align, const char *line);
2018-12-22 23:46:38 +01:00
void calcTextDimensions(const char *text, int size, int *w, int *h);
void useFont(char *name);
static void initChars(Font *f);
static char *nextCharacter(const char *str, int *i);
static Glyph *findGlyph(char *c);
static char drawTextBuffer[1024];
static Font fontHead;
static Font *fontTail;
static Font *activeFont = NULL;
static float scale;
2018-01-21 10:31:38 +01:00
void initFonts(void)
{
2018-12-22 23:46:38 +01:00
memset(&fontHead, 0, sizeof(Font));
fontTail = &fontHead;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
initFont("roboto", getFileLocation("gfx/fonts/Roboto-Medium.ttf"));
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
useFont("roboto");
2018-01-21 10:31:38 +01:00
}
2018-12-22 23:46:38 +01:00
static void initFont(char *name, char *filename)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
SDL_Texture *texture;
TTF_Font *font;
Font *f;
SDL_Surface *surface, *text;
SDL_Rect dest;
Glyph *g;
int i;
2018-12-23 16:32:33 +01:00
SDL_Color white = {255, 255, 255, 255};
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
f = malloc(sizeof(Font));
memset(f, 0, sizeof(Font));
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
font = TTF_OpenFont(filename, FONT_SIZE);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
initChars(f);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
surface = SDL_CreateRGBSurface(0, FONT_TEXTURE_SIZE, FONT_TEXTURE_SIZE, 32, 0, 0, 0, 0xff);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
SDL_SetColorKey(surface, SDL_TRUE, SDL_MapRGBA(surface->format, 0, 0, 0, 0));
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
dest.x = dest.y = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (i = 0 ; i < NUM_GLYPH_BUCKETS ; i++)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
for (g = f->glyphHead[i].next ; g != NULL ; g = g->next)
{
text = TTF_RenderUTF8_Blended(font, g->character, white);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
TTF_SizeText(font, g->character, &dest.w, &dest.h);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (dest.x + dest.w >= FONT_TEXTURE_SIZE)
{
dest.x = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
dest.y += dest.h + 1;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
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);
exit(1);
}
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
SDL_BlitSurface(text, NULL, surface, &dest);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
g->rect = dest;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
SDL_FreeSurface(text);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
dest.x += dest.w;
}
2018-01-21 10:31:38 +01:00
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
TTF_CloseFont(font);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
texture = toTexture(surface, 1);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
f->texture = texture;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
strcpy(f->name, name);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
fontTail->next = f;
fontTail = f;
2018-01-21 10:31:38 +01:00
}
2018-12-22 23:46:38 +01:00
static void initChars(Font *f)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
char *characters, *character;
Glyph *g, *glyphTail;
int i, bucket;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
characters = readFile("data/locale/characters.dat");
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
i = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(characters, &i);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
while (character)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
bucket = hashcode(character) % NUM_GLYPH_BUCKETS;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
glyphTail = &f->glyphHead[bucket];
2018-01-21 10:31:38 +01:00
2018-12-22 23:46:38 +01:00
/* horrible bit to look for the tail */
while (glyphTail->next)
{
glyphTail = glyphTail->next;
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
g = malloc(sizeof(Glyph));
memset(g, 0, sizeof(Glyph));
glyphTail->next = g;
glyphTail = g;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
STRNCPY(g->character, character, MAX_NAME_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(characters, &i);
2018-01-21 10:31:38 +01:00
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
free(characters);
}
2018-01-21 10:31:38 +01:00
2018-12-22 23:46:38 +01:00
void drawText(int x, int y, int size, int align, SDL_Color color, const char *format, ...)
{
va_list args;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (activeFont)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
memset(&drawTextBuffer, '\0', sizeof(drawTextBuffer));
2018-01-21 10:31:38 +01:00
2018-12-22 23:46:38 +01:00
va_start(args, format);
vsprintf(drawTextBuffer, format, args);
va_end(args);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (app.textWidth == 0)
{
#if !defined(__amigaos4__) && !defined(__morphos__)
2018-12-23 16:32:33 +01:00
SDL_SetTextureColorMod(activeFont->texture, 0, 0, 0);
SDL_SetTextureAlphaMod(activeFont->texture, 255);
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
drawTextLine(x + 2, y + 2, size, align, drawTextBuffer);
drawTextLine(x + 1, y + 1, size, align, drawTextBuffer);
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
SDL_SetTextureColorMod(activeFont->texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(activeFont->texture, color.a);
#endif
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
drawTextLine(x, y, size, align, drawTextBuffer);
2018-12-22 23:46:38 +01:00
}
else
{
#if !defined(__amigaos4__) && !defined(__morphos__)
2018-12-23 16:32:33 +01:00
SDL_SetTextureColorMod(activeFont->texture, 0, 0, 0);
SDL_SetTextureAlphaMod(activeFont->texture, 255);
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
drawTextLines(x + 2, y + 2, size, align);
drawTextLines(x + 1, y + 1, size, align);
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
SDL_SetTextureColorMod(activeFont->texture, color.r, color.g, color.b);
SDL_SetTextureAlphaMod(activeFont->texture, color.a);
#endif
2018-12-23 16:32:33 +01:00
drawTextLines(x, y, size, align);
2018-12-22 23:46:38 +01:00
}
}
2018-01-21 10:31:38 +01:00
}
2018-12-23 16:32:33 +01:00
static void drawTextLines(int x, int y, int size, int align)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
char line[MAX_LINE_LENGTH], token[MAX_WORD_LENGTH];
int i, n, w, h, currentWidth, len;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(&line, '\0', sizeof(line));
memset(&token, '\0', sizeof(token));
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
len = strlen(drawTextBuffer);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = currentWidth = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (i = 0 ; i < len ; i++)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
token[n++] = drawTextBuffer[i];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (drawTextBuffer[i] == ' ' || i == len - 1)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
calcTextDimensions(token, size, &w, &h);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (currentWidth + w > app.textWidth)
{
2018-12-23 16:32:33 +01:00
drawTextLine(x, y, size, align, line);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
currentWidth = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
y += h;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(&line, '\0', sizeof(line));
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
strcat(line, token);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(&token, '\0', sizeof(token));
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
currentWidth += w;
2018-01-21 10:31:38 +01:00
}
}
2019-06-02 17:13:34 +02:00
2018-12-23 16:32:33 +01:00
drawTextLine(x, y, size, align, line);
2018-01-21 10:31:38 +01:00
}
2018-12-23 16:32:33 +01:00
static void drawTextLine(int x, int y, int size, int align, const char *line)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
int i, startX, n, w, h;
char word[MAX_WORD_LENGTH];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
scale = size / (FONT_SIZE * 1.0f);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
startX = x;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(word, 0, MAX_WORD_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
calcTextDimensions(line, size, &w, &h);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (align == TA_RIGHT)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
x -= w;
}
else if (align == TA_CENTER)
{
x -= (w / 2);
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (i = 0 ; i < strlen(line) ; i++)
{
word[n++] = line[i];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (line[i] == ' ')
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
drawWord(word, &x, &y, startX);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(word, 0, MAX_WORD_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = 0;
2018-01-21 10:31:38 +01:00
}
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
drawWord(word, &x, &y, startX);
2018-01-21 10:31:38 +01:00
}
2018-12-22 23:46:38 +01:00
static void drawWord(char *word, int *x, int *y, int startX)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
int i;
char *character;
SDL_Rect dest;
Glyph *g;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
i = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(word, &i);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
while (character)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
g = findGlyph(character);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
dest.x = *x;
dest.y = *y;
dest.w = g->rect.w * scale;
dest.h = g->rect.h * scale;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
SDL_RenderCopy(app.renderer, activeFont->texture, &g->rect, &dest);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
*x += g->rect.w * scale;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(word, &i);
2018-01-21 10:31:38 +01:00
}
}
2018-12-22 23:46:38 +01:00
static Glyph *findGlyph(char *c)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
Glyph *g;
int bucket;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
bucket = hashcode(c) % NUM_GLYPH_BUCKETS;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (g = activeFont->glyphHead[bucket].next ; g != NULL ; g = g->next)
{
if (strcmp(g->character, c) == 0)
{
return g;
}
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
SDL_LogMessage(SDL_LOG_CATEGORY_APPLICATION, SDL_LOG_PRIORITY_CRITICAL, "Couldn't find glyph for '%s'", c);
exit(1);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
return NULL;
2018-01-21 10:31:38 +01:00
}
2018-12-22 23:46:38 +01:00
void useFont(char *name)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
Font *f;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (f = fontHead.next ; f != NULL ; f = f->next)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
if (strcmp(f->name, name) == 0)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
activeFont = f;
return;
2018-01-21 10:31:38 +01:00
}
}
}
2018-12-22 23:46:38 +01:00
void calcTextDimensions(const char *text, int size, int *w, int *h)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
float scale;
2018-01-21 10:31:38 +01:00
int i;
2018-12-22 23:46:38 +01:00
char *character;
Glyph *g;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
scale = size / (FONT_SIZE * 1.0f);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
*w = 0;
*h = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
i = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(text, &i);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
while (character)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
g = findGlyph(character);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
*w += g->rect.w * scale;
*h = MAX(g->rect.h * scale, *h);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character = nextCharacter(text, &i);
2018-01-21 10:31:38 +01:00
}
}
2018-12-22 23:46:38 +01:00
int getWrappedTextHeight(char *text, int size)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
char word[MAX_WORD_LENGTH];
int i, y, n, w, h, currentWidth, len;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
STRNCPY(drawTextBuffer, text, MAX_LINE_LENGTH);
2019-06-02 17:13:34 +02:00
2018-01-21 10:31:38 +01:00
n = 0;
2018-12-22 23:46:38 +01:00
y = 0;
h = 0;
currentWidth = 0;
len = strlen(drawTextBuffer);
memset(word, 0, MAX_WORD_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
for (i = 0 ; i < len ; i++)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
word[n++] = drawTextBuffer[i];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (drawTextBuffer[i] == ' ' || i == len - 1)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
calcTextDimensions(word, size, &w, &h);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if (currentWidth + w > app.textWidth)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
currentWidth = 0;
y += h;
2018-01-21 10:31:38 +01:00
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
currentWidth += w;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(word, 0, MAX_WORD_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = 0;
2018-01-21 10:31:38 +01:00
}
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
return y + h;
2018-01-21 10:31:38 +01:00
}
2018-12-22 23:46:38 +01:00
static char *nextCharacter(const char *str, int *i)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
static char character[MAX_NAME_LENGTH];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
unsigned char bit;
int n;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
memset(character, '\0', MAX_NAME_LENGTH);
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
n = 0;
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
while (1)
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
bit = (unsigned char)str[*i];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
if ((bit >= ' ' && bit <= '~') || bit >= 0xC0 || bit == '\0')
2018-01-21 10:31:38 +01:00
{
2018-12-22 23:46:38 +01:00
if (n > 0)
{
return character[0] != '\0' ? character : NULL;
}
2018-01-21 10:31:38 +01:00
}
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
character[n++] = str[*i];
2019-06-02 17:13:34 +02:00
2018-12-22 23:46:38 +01:00
*i = *i + 1;
2018-01-21 10:31:38 +01:00
}
}