2018-12-06 09:37:19 +01:00
/*
2022-07-30 17:10:02 +02:00
Copyright ( C ) 2018 - 2019 , 2022 Parallel Realities
2018-12-06 09:37:19 +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 .
*/
2022-07-30 17:10:02 +02:00
# include <SDL2/SDL_ttf.h>
2022-07-31 11:43:20 +02:00
# include "../common.h"
2022-07-30 17:10:02 +02:00
# include "../system/io.h"
2022-07-31 11:43:20 +02:00
# include "../system/textures.h"
# include "text.h"
2022-07-30 17:10:02 +02:00
2022-07-31 11:43:20 +02:00
# define FONT_SIZE 32
# define FONT_TEXTURE_SIZE 512
# define MAX_WORD_LENGTH 128
# define MAX_GLYPH_SIZE 8
2022-07-30 17:10:02 +02:00
extern App app ;
2018-12-06 09:37:19 +01:00
2021-05-01 14:35:33 +02:00
static void initFont ( char * name , char * filename , char * characters ) ;
2018-12-06 09:37:19 +01:00
static void drawWord ( char * word , int * x , int * y , int startX ) ;
2018-12-23 16:52:38 +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 ) ;
2022-07-31 11:43:20 +02:00
static int nextGlyph ( const char * str , int * i , char * glyphBuffer ) ;
2018-12-06 09:37:19 +01:00
2022-07-31 11:43:20 +02:00
static char drawTextBuffer [ 1024 ] ;
static Font fontHead ;
2018-12-06 09:37:19 +01:00
static Font * fontTail ;
static Font * activeFont = NULL ;
static float scale ;
void initFonts ( void )
{
2021-05-01 14:35:33 +02:00
char * characters ;
2018-12-06 09:37:19 +01:00
memset ( & fontHead , 0 , sizeof ( Font ) ) ;
fontTail = & fontHead ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
characters = readFile ( " data/locale/characters.dat " ) ;
initFont ( " roboto " , getFileLocation ( " data/fonts/Roboto-Medium.ttf " ) , characters ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
initFont ( " khosrau " , getFileLocation ( " data/fonts/Khosrau.ttf " ) , characters ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
useFont ( " roboto " ) ;
2021-05-01 14:35:33 +02:00
free ( characters ) ;
2018-12-06 09:37:19 +01:00
}
2021-05-01 14:35:33 +02:00
static void initFont ( char * name , char * filename , char * characters )
2018-12-06 09:37:19 +01:00
{
SDL_Texture * texture ;
2022-07-31 11:43:20 +02:00
TTF_Font * font ;
Font * f ;
2018-12-06 09:37:19 +01:00
SDL_Surface * surface , * text ;
2022-07-31 11:43:20 +02:00
SDL_Rect dest ;
int i , n , largest ;
char glyphBuffer [ MAX_GLYPH_SIZE ] ;
SDL_Color white = { 255 , 255 , 255 , 255 } ;
2019-11-06 20:18:03 +01:00
2018-12-08 20:07:15 +01:00
f = malloc ( sizeof ( Font ) ) ;
memset ( f , 0 , sizeof ( Font ) ) ;
2019-11-06 20:18:03 +01:00
2018-12-10 09:42:09 +01:00
font = TTF_OpenFont ( filename , FONT_SIZE ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
surface = SDL_CreateRGBSurface ( 0 , FONT_TEXTURE_SIZE , FONT_TEXTURE_SIZE , 32 , 0 , 0 , 0 , 0xff ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
SDL_SetColorKey ( surface , SDL_TRUE , SDL_MapRGBA ( surface - > format , 0 , 0 , 0 , 0 ) ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
dest . x = dest . y = 0 ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
largest = 0 ;
i = 0 ;
2021-05-01 17:00:35 +02:00
while ( ( n = nextGlyph ( characters , & i , glyphBuffer ) ) ! = 0 )
2018-12-06 09:37:19 +01:00
{
2021-05-01 14:35:33 +02:00
largest = MAX ( largest , n ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
text = TTF_RenderUTF8_Blended ( font , glyphBuffer , white ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
TTF_SizeText ( font , glyphBuffer , & dest . w , & dest . h ) ;
if ( dest . x + dest . w > = FONT_TEXTURE_SIZE )
{
dest . x = 0 ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
dest . y + = dest . h + 1 ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02: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 ) ;
2018-12-08 20:07:15 +01:00
}
2021-05-01 14:35:33 +02:00
}
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
SDL_BlitSurface ( text , NULL , surface , & dest ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
f - > glyphs [ n ] = dest ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
SDL_FreeSurface ( text ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
dest . x + = dest . w ;
2018-12-06 09:37:19 +01:00
}
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
TTF_CloseFont ( font ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
texture = toTexture ( surface , 1 ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
f - > texture = texture ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
strcpy ( f - > name , name ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
fontTail - > next = f ;
fontTail = f ;
}
void drawText ( int x , int y , int size , int align , SDL_Color color , const char * format , . . . )
{
va_list args ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
if ( activeFont )
{
SDL_SetTextureColorMod ( activeFont - > texture , color . r , color . g , color . b ) ;
SDL_SetTextureAlphaMod ( activeFont - > texture , color . a ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
memset ( & drawTextBuffer , ' \0 ' , sizeof ( drawTextBuffer ) ) ;
va_start ( args , format ) ;
vsprintf ( drawTextBuffer , format , args ) ;
va_end ( args ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( app . textWidth = = 0 )
2018-12-06 09:37:19 +01:00
{
2018-12-23 16:52:38 +01:00
drawTextLine ( x , y , size , align , drawTextBuffer ) ;
2018-12-06 09:37:19 +01:00
}
2018-12-07 15:49:08 +01:00
else
2018-12-06 09:37:19 +01:00
{
2018-12-23 16:52:38 +01:00
drawTextLines ( x , y , size , align ) ;
2018-12-06 09:37:19 +01:00
}
2018-12-07 15:49:08 +01:00
}
}
2018-12-23 16:52:38 +01:00
static void drawTextLines ( int x , int y , int size , int align )
2018-12-07 15:49:08 +01:00
{
char line [ MAX_LINE_LENGTH ] , token [ MAX_WORD_LENGTH ] ;
2022-07-31 11:43:20 +02:00
int i , n , w , h , currentWidth , len ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( & line , ' \0 ' , sizeof ( line ) ) ;
memset ( & token , ' \0 ' , sizeof ( token ) ) ;
2019-11-06 20:18:03 +01:00
2018-12-16 17:43:15 +01:00
len = strlen ( drawTextBuffer ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = currentWidth = 0 ;
2019-11-06 20:18:03 +01:00
2022-07-31 11:43:20 +02:00
for ( i = 0 ; i < len ; i + + )
2018-12-07 15:49:08 +01:00
{
token [ n + + ] = drawTextBuffer [ i ] ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( drawTextBuffer [ i ] = = ' ' | | i = = len - 1 )
2018-12-06 09:37:19 +01:00
{
2018-12-07 15:49:08 +01:00
calcTextDimensions ( token , size , & w , & h ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( currentWidth + w > app . textWidth )
2018-12-06 09:37:19 +01:00
{
2018-12-23 16:52:38 +01:00
drawTextLine ( x , y , size , align , line ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
currentWidth = 0 ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
y + = h ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( & line , ' \0 ' , sizeof ( line ) ) ;
2018-12-06 09:37:19 +01:00
}
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
strcat ( line , token ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = 0 ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( & token , ' \0 ' , sizeof ( token ) ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
currentWidth + = w ;
2018-12-06 09:37:19 +01:00
}
2018-12-07 15:49:08 +01:00
}
2019-11-06 20:18:03 +01:00
2018-12-23 16:52:38 +01:00
drawTextLine ( x , y , size , align , line ) ;
2018-12-07 15:49:08 +01:00
}
2018-12-23 16:52:38 +01:00
static void drawTextLine ( int x , int y , int size , int align , const char * line )
2018-12-07 15:49:08 +01:00
{
2022-07-31 11:43:20 +02:00
int i , startX , n , w , h ;
2018-12-07 15:49:08 +01:00
char word [ MAX_WORD_LENGTH ] ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
scale = size / ( FONT_SIZE * 1.0f ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
startX = x ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( word , 0 , MAX_WORD_LENGTH ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = 0 ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
calcTextDimensions ( line , size , & w , & h ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( align = = TA_RIGHT )
{
x - = w ;
2018-12-06 09:37:19 +01:00
}
2018-12-07 15:49:08 +01:00
else if ( align = = TA_CENTER )
{
x - = ( w / 2 ) ;
}
2019-11-06 20:18:03 +01:00
2022-07-31 11:43:20 +02:00
for ( i = 0 ; i < strlen ( line ) ; i + + )
2018-12-07 15:49:08 +01:00
{
word [ n + + ] = line [ i ] ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( line [ i ] = = ' ' )
{
drawWord ( word , & x , & y , startX ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( word , 0 , MAX_WORD_LENGTH ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = 0 ;
}
}
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
drawWord ( word , & x , & y , startX ) ;
2018-12-06 09:37:19 +01:00
}
static void drawWord ( char * word , int * x , int * y , int startX )
{
2022-07-31 11:43:20 +02:00
int i , n ;
2018-12-06 09:37:19 +01:00
SDL_Rect dest ;
2019-11-06 20:18:03 +01:00
2018-12-08 20:07:15 +01:00
i = 0 ;
2019-11-06 20:18:03 +01:00
2021-05-01 17:00:35 +02:00
while ( ( n = nextGlyph ( word , & i , NULL ) ) ! = 0 )
2018-12-06 09:37:19 +01:00
{
dest . x = * x ;
dest . y = * y ;
2021-05-01 14:35:33 +02:00
dest . w = activeFont - > glyphs [ n ] . w * scale ;
dest . h = activeFont - > glyphs [ n ] . h * scale ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
SDL_RenderCopy ( app . renderer , activeFont - > texture , & activeFont - > glyphs [ n ] , & dest ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
* x + = activeFont - > glyphs [ n ] . w * scale ;
2018-12-06 09:37:19 +01:00
}
}
void useFont ( char * name )
{
Font * f ;
2019-11-06 20:18:03 +01:00
2022-07-31 11:43:20 +02:00
for ( f = fontHead . next ; f ! = NULL ; f = f - > next )
2018-12-06 09:37:19 +01:00
{
if ( strcmp ( f - > name , name ) = = 0 )
{
activeFont = f ;
return ;
}
}
}
2018-12-07 15:49:08 +01:00
void calcTextDimensions ( const char * text , int size , int * w , int * h )
2018-12-06 09:37:19 +01:00
{
float scale ;
2022-07-31 11:43:20 +02:00
int i , n ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
scale = size / ( FONT_SIZE * 1.0f ) ;
2019-11-06 20:18:03 +01:00
2018-12-06 09:37:19 +01:00
* w = 0 ;
* h = 0 ;
2019-11-06 20:18:03 +01:00
2018-12-08 20:07:15 +01:00
i = 0 ;
2019-11-06 20:18:03 +01:00
2021-05-01 17:00:35 +02:00
while ( ( n = nextGlyph ( text , & i , NULL ) ) ! = 0 )
2018-12-06 09:37:19 +01:00
{
2021-05-01 14:35:33 +02:00
* w + = activeFont - > glyphs [ n ] . w * scale ;
* h = MAX ( activeFont - > glyphs [ n ] . h * scale , * h ) ;
2018-12-06 09:37:19 +01:00
}
}
int getWrappedTextHeight ( char * text , int size )
{
2018-12-07 15:49:08 +01:00
char word [ MAX_WORD_LENGTH ] ;
2022-07-31 11:43:20 +02:00
int i , y , n , w , h , currentWidth , len ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
STRNCPY ( drawTextBuffer , text , MAX_LINE_LENGTH ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = 0 ;
y = 0 ;
h = 0 ;
currentWidth = 0 ;
2018-12-16 18:23:20 +01:00
len = strlen ( drawTextBuffer ) ;
2018-12-07 15:49:08 +01:00
memset ( word , 0 , MAX_WORD_LENGTH ) ;
2019-11-06 20:18:03 +01:00
2022-07-31 11:43:20 +02:00
for ( i = 0 ; i < len ; i + + )
2018-12-06 09:37:19 +01:00
{
2018-12-07 15:49:08 +01:00
word [ n + + ] = drawTextBuffer [ i ] ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( drawTextBuffer [ i ] = = ' ' | | i = = len - 1 )
2018-12-06 09:37:19 +01:00
{
2018-12-07 15:49:08 +01:00
calcTextDimensions ( word , size , & w , & h ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
if ( currentWidth + w > app . textWidth )
{
currentWidth = 0 ;
y + = h ;
}
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
currentWidth + = w ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
memset ( word , 0 , MAX_WORD_LENGTH ) ;
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
n = 0 ;
}
2018-12-06 09:37:19 +01:00
}
2019-11-06 20:18:03 +01:00
2018-12-07 15:49:08 +01:00
return y + h ;
2018-12-06 09:37:19 +01:00
}
2018-12-08 20:07:15 +01:00
2021-05-01 14:35:33 +02:00
static int nextGlyph ( const char * str , int * i , char * glyphBuffer )
2018-12-08 20:07:15 +01:00
{
2022-07-31 11:43:20 +02:00
int len ;
unsigned bit ;
2021-05-01 17:00:35 +02:00
const char * p ;
2019-11-06 20:18:03 +01:00
2022-07-31 11:43:20 +02:00
bit = ( unsigned char ) str [ * i ] ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
if ( bit < ' ' )
{
return 0 ;
}
len = 1 ;
if ( bit > = 0xF0 )
{
2022-07-31 11:43:20 +02:00
bit = ( int ) ( str [ * i ] & 0x07 ) < < 18 ;
2021-05-01 14:35:33 +02:00
bit | = ( int ) ( str [ * i + 1 ] & 0x3F ) < < 12 ;
bit | = ( int ) ( str [ * i + 2 ] & 0x3F ) < < 6 ;
bit | = ( int ) ( str [ * i + 3 ] & 0x3F ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
len = 4 ;
}
else if ( bit > = 0xE0 )
{
2022-07-31 11:43:20 +02:00
bit = ( int ) ( str [ * i ] & 0x0F ) < < 12 ;
2021-05-01 14:35:33 +02:00
bit | = ( int ) ( str [ * i + 1 ] & 0x3F ) < < 6 ;
bit | = ( int ) ( str [ * i + 2 ] & 0x3F ) ;
len = 3 ;
}
else if ( bit > = 0xC0 )
2019-11-06 20:18:03 +01:00
{
2022-07-31 11:43:20 +02:00
bit = ( int ) ( str [ * i ] & 0x1F ) < < 6 ;
2021-05-01 14:35:33 +02:00
bit | = ( int ) ( str [ * i + 1 ] & 0x3F ) ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
len = 2 ;
}
/* only fill the buffer if it's been supplied */
if ( glyphBuffer ! = NULL )
{
2021-05-01 17:00:35 +02:00
p = str + * i ;
2021-05-01 14:35:33 +02:00
memset ( glyphBuffer , 0 , MAX_GLYPH_SIZE ) ;
2021-05-01 17:00:35 +02:00
memcpy ( glyphBuffer , p , len ) ;
if ( bit > = MAX_GLYPHS )
2020-03-28 14:52:17 +01:00
{
2021-05-01 17:00:35 +02:00
printf ( " Glyph '%s' index exceeds array size (%d >= %d) \n " , glyphBuffer , bit , MAX_GLYPHS ) ;
exit ( 1 ) ;
2020-03-28 14:52:17 +01:00
}
2021-05-01 14:35:33 +02:00
}
2020-01-22 08:27:35 +01:00
2021-05-01 14:35:33 +02:00
* i = * i + len ;
2019-11-06 20:18:03 +01:00
2021-05-01 14:35:33 +02:00
return bit ;
2018-12-08 20:07:15 +01:00
}