2019-12-28 12:16:32 +01:00
# include <stdio.h>
2020-05-14 23:28:22 +02:00
# include <stdbool.h>
2022-02-03 01:50:43 +01:00
# include <stdint.h>
2019-12-28 12:16:32 +01:00
# include <assert.h>
# include <math.h>
2021-09-11 04:22:30 +02:00
# include <ft2build.h>
# include <freetype/ftlcdfil.h>
# include <freetype/ftoutln.h>
# include FT_FREETYPE_H
2022-11-14 15:01:24 +01:00
# ifdef _WIN32
2022-11-17 06:09:38 +01:00
# include <windows.h>
2022-11-14 15:01:24 +01:00
# include "utfconv.h"
# endif
2021-04-29 14:15:24 +02:00
# include "renderer.h"
# include "renwindow.h"
2019-12-28 12:16:32 +01:00
# define MAX_GLYPHSET 256
2021-11-01 15:03:36 +01:00
# define MAX_LOADABLE_GLYPHSETS 1024
2021-09-11 04:22:30 +02:00
# define SUBPIXEL_BITMAPS_CACHED 3
2019-12-28 12:16:32 +01:00
2021-04-29 14:15:24 +02:00
static RenWindow window_renderer = { 0 } ;
2021-09-11 04:22:30 +02:00
static FT_Library library ;
2019-12-28 12:16:32 +01:00
2022-04-20 23:13:42 +02:00
// draw_rect_surface is used as a 1x1 surface to simplify ren_draw_rect with blending
static SDL_Surface * draw_rect_surface ;
2020-05-08 14:41:39 +02:00
static void * check_alloc ( void * ptr ) {
if ( ! ptr ) {
fprintf ( stderr , " Fatal error: memory allocation failed \n " ) ;
exit ( EXIT_FAILURE ) ;
}
return ptr ;
}
2021-09-11 04:22:30 +02:00
/************************* Fonts *************************/
typedef struct {
2021-10-13 05:24:52 +02:00
unsigned short x0 , x1 , y0 , y1 , loaded ;
2021-09-11 04:22:30 +02:00
short bitmap_left , bitmap_top ;
float xadvance ;
} GlyphMetric ;
typedef struct {
2021-10-02 03:20:44 +02:00
SDL_Surface * surface ;
2022-06-12 02:22:01 +02:00
GlyphMetric metrics [ MAX_GLYPHSET ] ;
2021-09-11 04:22:30 +02:00
} GlyphSet ;
typedef struct RenFont {
FT_Face face ;
2021-10-31 18:34:46 +01:00
GlyphSet * sets [ SUBPIXEL_BITMAPS_CACHED ] [ MAX_LOADABLE_GLYPHSETS ] ;
2021-09-25 06:35:55 +02:00
float size , space_advance , tab_advance ;
2022-04-15 17:34:46 +02:00
unsigned short max_height , baseline , height ;
2021-11-23 00:13:43 +01:00
ERenFontAntialiasing antialiasing ;
2021-09-11 04:22:30 +02:00
ERenFontHinting hinting ;
2021-09-17 00:41:54 +02:00
unsigned char style ;
2022-08-20 22:15:08 +02:00
unsigned short underline_thickness ;
2022-11-17 06:09:38 +01:00
# ifdef _WIN32
2022-11-14 15:01:24 +01:00
unsigned char * file ;
2022-11-17 06:09:38 +01:00
HANDLE file_handle ;
# endif
2022-11-14 15:00:40 +01:00
char path [ ] ;
2021-09-11 04:22:30 +02:00
} RenFont ;
2020-05-08 14:41:39 +02:00
2019-12-28 12:16:32 +01:00
static const char * utf8_to_codepoint ( const char * p , unsigned * dst ) {
2022-04-15 17:34:46 +02:00
const unsigned char * up = ( unsigned char * ) p ;
2019-12-28 12:16:32 +01:00
unsigned res , n ;
switch ( * p & 0xf0 ) {
2022-04-15 17:34:46 +02:00
case 0xf0 : res = * up & 0x07 ; n = 3 ; break ;
case 0xe0 : res = * up & 0x0f ; n = 2 ; break ;
2019-12-28 12:16:32 +01:00
case 0xd0 :
2022-04-15 17:34:46 +02:00
case 0xc0 : res = * up & 0x1f ; n = 1 ; break ;
default : res = * up ; n = 0 ; break ;
2019-12-28 12:16:32 +01:00
}
while ( n - - ) {
2022-04-15 17:34:46 +02:00
res = ( res < < 6 ) | ( * ( + + up ) & 0x3f ) ;
2019-12-28 12:16:32 +01:00
}
* dst = res ;
2022-04-15 17:34:46 +02:00
return ( const char * ) up + 1 ;
2019-12-28 12:16:32 +01:00
}
2021-09-11 04:22:30 +02:00
static int font_set_load_options ( RenFont * font ) {
2022-06-12 02:22:01 +02:00
int load_target = font - > antialiasing = = FONT_ANTIALIASING_NONE ? FT_LOAD_TARGET_MONO
2021-11-23 00:13:43 +01:00
: ( font - > hinting = = FONT_HINTING_SLIGHT ? FT_LOAD_TARGET_LIGHT : FT_LOAD_TARGET_NORMAL ) ;
int hinting = font - > hinting = = FONT_HINTING_NONE ? FT_LOAD_NO_HINTING : FT_LOAD_FORCE_AUTOHINT ;
return load_target | hinting ;
2021-03-31 17:33:35 +02:00
}
2021-09-11 04:22:30 +02:00
static int font_set_render_options ( RenFont * font ) {
2021-11-23 00:13:43 +01:00
if ( font - > antialiasing = = FONT_ANTIALIASING_NONE )
return FT_RENDER_MODE_MONO ;
if ( font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ) {
2021-09-27 01:46:32 +02:00
unsigned char weights [ ] = { 0x10 , 0x40 , 0x70 , 0x40 , 0x10 } ;
2021-09-11 04:22:30 +02:00
switch ( font - > hinting ) {
case FONT_HINTING_NONE : FT_Library_SetLcdFilter ( library , FT_LCD_FILTER_NONE ) ; break ;
2021-09-27 01:46:32 +02:00
case FONT_HINTING_SLIGHT :
2021-09-27 02:09:51 +02:00
case FONT_HINTING_FULL : FT_Library_SetLcdFilterWeights ( library , weights ) ; break ;
2021-09-11 04:22:30 +02:00
}
return FT_RENDER_MODE_LCD ;
} else {
switch ( font - > hinting ) {
case FONT_HINTING_NONE : return FT_RENDER_MODE_NORMAL ; break ;
case FONT_HINTING_SLIGHT : return FT_RENDER_MODE_LIGHT ; break ;
case FONT_HINTING_FULL : return FT_RENDER_MODE_LIGHT ; break ;
2021-03-31 17:33:35 +02:00
}
}
2021-09-11 04:22:30 +02:00
return 0 ;
2019-12-28 12:16:32 +01:00
}
2021-09-17 00:41:54 +02:00
static int font_set_style ( FT_Outline * outline , int x_translation , unsigned char style ) {
FT_Outline_Translate ( outline , x_translation , 0 ) ;
2022-08-20 22:15:08 +02:00
if ( style & FONT_STYLE_SMOOTH )
FT_Outline_Embolden ( outline , 1 < < 5 ) ;
2021-09-17 00:41:54 +02:00
if ( style & FONT_STYLE_BOLD )
FT_Outline_EmboldenXY ( outline , 1 < < 5 , 0 ) ;
if ( style & FONT_STYLE_ITALIC ) {
FT_Matrix matrix = { 1 < < 16 , 1 < < 14 , 0 , 1 < < 16 } ;
FT_Outline_Transform ( outline , & matrix ) ;
}
return 0 ;
}
2021-10-17 06:39:08 +02:00
static void font_load_glyphset ( RenFont * font , int idx ) {
2021-09-11 04:22:30 +02:00
unsigned int render_option = font_set_render_options ( font ) , load_option = font_set_load_options ( font ) ;
2021-11-23 00:13:43 +01:00
int bitmaps_cached = font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1 ;
unsigned int byte_width = font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? 3 : 1 ;
2021-10-02 03:20:44 +02:00
for ( int j = 0 , pen_x = 0 ; j < bitmaps_cached ; + + j ) {
GlyphSet * set = check_alloc ( calloc ( 1 , sizeof ( GlyphSet ) ) ) ;
2021-10-02 05:56:36 +02:00
font - > sets [ j ] [ idx ] = set ;
2021-10-31 18:27:51 +01:00
for ( int i = 0 ; i < MAX_GLYPHSET ; + + i ) {
2021-10-13 05:24:52 +02:00
int glyph_index = FT_Get_Char_Index ( font - > face , i + idx * MAX_GLYPHSET ) ;
2022-02-15 21:57:07 +01:00
if ( ! glyph_index | | FT_Load_Glyph ( font - > face , glyph_index , load_option | FT_LOAD_BITMAP_METRICS_ONLY )
| | font_set_style ( & font - > face - > glyph - > outline , j * ( 64 / SUBPIXEL_BITMAPS_CACHED ) , font - > style ) | | FT_Render_Glyph ( font - > face - > glyph , render_option ) ) {
2021-10-02 03:20:44 +02:00
continue ;
2022-02-15 21:57:07 +01:00
}
2021-10-02 03:20:44 +02:00
FT_GlyphSlot slot = font - > face - > glyph ;
int glyph_width = slot - > bitmap . width / byte_width ;
2021-11-23 00:13:43 +01:00
if ( font - > antialiasing = = FONT_ANTIALIASING_NONE )
glyph_width * = 8 ;
2021-10-13 05:24:52 +02:00
set - > metrics [ i ] = ( GlyphMetric ) { pen_x , pen_x + glyph_width , 0 , slot - > bitmap . rows , true , slot - > bitmap_left , slot - > bitmap_top , ( slot - > advance . x + slot - > lsb_delta - slot - > rsb_delta ) / 64.0f } ;
2021-10-02 03:20:44 +02:00
pen_x + = glyph_width ;
font - > max_height = slot - > bitmap . rows > font - > max_height ? slot - > bitmap . rows : font - > max_height ;
2022-02-15 21:57:07 +01:00
// In order to fix issues with monospacing; we need the unhinted xadvance; as FreeType doesn't correctly report the hinted advance for spaces on monospace fonts (like RobotoMono). See #843.
if ( ! glyph_index | | FT_Load_Glyph ( font - > face , glyph_index , ( load_option | FT_LOAD_BITMAP_METRICS_ONLY | FT_LOAD_NO_HINTING ) & ~ FT_LOAD_FORCE_AUTOHINT )
| | font_set_style ( & font - > face - > glyph - > outline , j * ( 64 / SUBPIXEL_BITMAPS_CACHED ) , font - > style ) | | FT_Render_Glyph ( font - > face - > glyph , render_option ) ) {
continue ;
}
slot = font - > face - > glyph ;
set - > metrics [ i ] . xadvance = slot - > advance . x / 64.0f ;
2021-10-02 03:20:44 +02:00
}
2021-10-02 05:56:36 +02:00
if ( pen_x = = 0 )
continue ;
2021-11-23 00:13:43 +01:00
set - > surface = check_alloc ( SDL_CreateRGBSurface ( 0 , pen_x , font - > max_height , font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? 24 : 8 , 0 , 0 , 0 , 0 ) ) ;
2022-04-15 17:34:46 +02:00
uint8_t * pixels = set - > surface - > pixels ;
2021-10-31 18:27:51 +01:00
for ( int i = 0 ; i < MAX_GLYPHSET ; + + i ) {
2021-09-11 04:22:30 +02:00
int glyph_index = FT_Get_Char_Index ( font - > face , i + idx * MAX_GLYPHSET ) ;
if ( ! glyph_index | | FT_Load_Glyph ( font - > face , glyph_index , load_option ) )
continue ;
FT_GlyphSlot slot = font - > face - > glyph ;
2021-09-17 00:41:54 +02:00
font_set_style ( & slot - > outline , ( 64 / bitmaps_cached ) * j , font - > style ) ;
2021-09-11 04:22:30 +02:00
if ( FT_Render_Glyph ( slot , render_option ) )
2022-06-12 02:22:01 +02:00
continue ;
2022-04-15 17:34:46 +02:00
for ( unsigned int line = 0 ; line < slot - > bitmap . rows ; + + line ) {
2021-10-02 03:20:44 +02:00
int target_offset = set - > surface - > pitch * line + set - > metrics [ i ] . x0 * byte_width ;
2021-09-11 04:22:30 +02:00
int source_offset = line * slot - > bitmap . pitch ;
2021-11-23 00:13:43 +01:00
if ( font - > antialiasing = = FONT_ANTIALIASING_NONE ) {
2022-04-15 17:34:46 +02:00
for ( unsigned int column = 0 ; column < slot - > bitmap . width ; + + column ) {
2021-11-23 00:13:43 +01:00
int current_source_offset = source_offset + ( column / 8 ) ;
int source_pixel = slot - > bitmap . buffer [ current_source_offset ] ;
pixels [ + + target_offset ] = ( ( source_pixel > > ( 7 - ( column % 8 ) ) ) & 0x1 ) < < 7 ;
}
} else
memcpy ( & pixels [ target_offset ] , & slot - > bitmap . buffer [ source_offset ] , slot - > bitmap . width ) ;
2021-09-11 04:22:30 +02:00
}
}
2020-05-14 23:28:22 +02:00
}
2019-12-28 12:16:32 +01:00
}
2021-10-02 05:56:36 +02:00
static GlyphSet * font_get_glyphset ( RenFont * font , unsigned int codepoint , int subpixel_idx ) {
2021-10-31 18:34:46 +01:00
int idx = ( codepoint > > 8 ) % MAX_LOADABLE_GLYPHSETS ;
2021-11-23 00:13:43 +01:00
if ( ! font - > sets [ font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0 ] [ idx ] )
2021-10-02 03:20:44 +02:00
font_load_glyphset ( font , idx ) ;
2021-11-23 00:13:43 +01:00
return font - > sets [ font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? subpixel_idx : 0 ] [ idx ] ;
2021-04-24 10:21:34 +02:00
}
2021-10-17 06:39:08 +02:00
static RenFont * font_group_get_glyph ( GlyphSet * * set , GlyphMetric * * metric , RenFont * * fonts , unsigned int codepoint , int bitmap_index ) {
2022-04-15 17:34:46 +02:00
if ( ! metric ) {
return NULL ;
}
2021-10-17 06:39:08 +02:00
if ( bitmap_index < 0 )
bitmap_index + = SUBPIXEL_BITMAPS_CACHED ;
for ( int i = 0 ; i < FONT_FALLBACK_MAX & & fonts [ i ] ; + + i ) {
* set = font_get_glyphset ( fonts [ i ] , codepoint , bitmap_index ) ;
* metric = & ( * set ) - > metrics [ codepoint % 256 ] ;
if ( ( * metric ) - > loaded | | codepoint < 0xFF )
return fonts [ i ] ;
}
2022-04-15 17:34:46 +02:00
if ( * metric & & ! ( * metric ) - > loaded & & codepoint > 0xFF & & codepoint ! = 0x25A1 )
2021-10-17 06:39:08 +02:00
return font_group_get_glyph ( set , metric , fonts , 0x25A1 , bitmap_index ) ;
return fonts [ 0 ] ;
2021-04-24 10:21:34 +02:00
}
2022-06-22 19:19:52 +02:00
static void font_clear_glyph_cache ( RenFont * font ) {
for ( int i = 0 ; i < SUBPIXEL_BITMAPS_CACHED ; + + i ) {
for ( int j = 0 ; j < MAX_GLYPHSET ; + + j ) {
if ( font - > sets [ i ] [ j ] ) {
if ( font - > sets [ i ] [ j ] - > surface )
SDL_FreeSurface ( font - > sets [ i ] [ j ] - > surface ) ;
free ( font - > sets [ i ] [ j ] ) ;
font - > sets [ i ] [ j ] = NULL ;
}
}
}
}
2021-11-23 00:13:43 +01:00
RenFont * ren_font_load ( const char * path , float size , ERenFontAntialiasing antialiasing , ERenFontHinting hinting , unsigned char style ) {
2022-11-14 15:01:24 +01:00
FT_Face face = NULL ;
# ifdef _WIN32
HANDLE file = INVALID_HANDLE_VALUE ;
DWORD read ;
int font_file_len = 0 ;
unsigned char * font_file = NULL ;
wchar_t * wpath = NULL ;
if ( ( wpath = utfconv_utf8towc ( path ) ) = = NULL )
2020-06-01 17:01:42 +02:00
return NULL ;
2022-11-14 15:01:24 +01:00
if ( ( file = CreateFileW ( wpath ,
GENERIC_READ ,
2022-11-17 06:09:38 +01:00
FILE_SHARE_READ , // or else we can't copy fonts
2022-11-14 15:01:24 +01:00
NULL ,
OPEN_EXISTING ,
FILE_ATTRIBUTE_NORMAL ,
NULL ) ) = = INVALID_HANDLE_VALUE )
goto failure ;
if ( ( font_file_len = GetFileSize ( file , NULL ) ) = = INVALID_FILE_SIZE )
goto failure ;
font_file = check_alloc ( malloc ( font_file_len * sizeof ( unsigned char ) ) ) ;
if ( ! ReadFile ( file , font_file , font_file_len , & read , NULL ) | | read ! = font_file_len )
goto failure ;
free ( wpath ) ;
2022-11-17 06:09:38 +01:00
wpath = NULL ;
2022-11-14 15:01:24 +01:00
if ( FT_New_Memory_Face ( library , font_file , read , 0 , & face ) )
goto failure ;
# else
if ( FT_New_Face ( library , path , 0 , & face ) )
return NULL ;
# endif
2021-09-22 03:49:32 +02:00
const int surface_scale = renwin_surface_scale ( & window_renderer ) ;
if ( FT_Set_Pixel_Sizes ( face , 0 , ( int ) ( size * surface_scale ) ) )
2021-09-11 04:22:30 +02:00
goto failure ;
int len = strlen ( path ) ;
RenFont * font = check_alloc ( calloc ( 1 , sizeof ( RenFont ) + len + 1 ) ) ;
strcpy ( font - > path , path ) ;
font - > face = face ;
font - > size = size ;
2022-02-15 21:57:07 +01:00
font - > height = ( short ) ( ( face - > height / ( float ) face - > units_per_EM ) * font - > size ) ;
2022-04-04 16:38:43 +02:00
font - > baseline = ( short ) ( ( face - > ascender / ( float ) face - > units_per_EM ) * font - > size ) ;
2021-11-23 00:13:43 +01:00
font - > antialiasing = antialiasing ;
2021-09-11 04:22:30 +02:00
font - > hinting = hinting ;
2021-09-17 00:41:54 +02:00
font - > style = style ;
2022-06-12 02:22:01 +02:00
2022-11-14 15:01:24 +01:00
# ifdef _WIN32
// we need to keep this for freetype
font - > file = font_file ;
2022-11-17 06:09:38 +01:00
font - > file_handle = file ;
2022-11-14 15:01:24 +01:00
# endif
2022-08-20 22:15:08 +02:00
if ( FT_IS_SCALABLE ( face ) )
font - > underline_thickness = ( unsigned short ) ( ( face - > underline_thickness / ( float ) face - > units_per_EM ) * font - > size ) ;
if ( ! font - > underline_thickness ) font - > underline_thickness = ceil ( ( double ) font - > height / 14.0 ) ;
2022-06-12 02:22:01 +02:00
if ( FT_Load_Char ( face , ' ' , font_set_load_options ( font ) ) )
goto failure ;
font - > space_advance = face - > glyph - > advance . x / 64.0f ;
2021-09-11 04:22:30 +02:00
font - > tab_advance = font - > space_advance * 2 ;
2019-12-28 12:16:32 +01:00
return font ;
2022-11-14 15:01:24 +01:00
failure :
# ifdef _WIN32
free ( wpath ) ;
free ( font_file ) ;
if ( file ! = INVALID_HANDLE_VALUE ) CloseHandle ( file ) ;
# endif
if ( face ! = NULL )
FT_Done_Face ( face ) ;
2021-09-11 04:22:30 +02:00
return NULL ;
2019-12-28 12:16:32 +01:00
}
2022-06-22 19:19:52 +02:00
RenFont * ren_font_copy ( RenFont * font , float size , ERenFontAntialiasing antialiasing , ERenFontHinting hinting , int style ) {
antialiasing = antialiasing = = - 1 ? font - > antialiasing : antialiasing ;
hinting = hinting = = - 1 ? font - > hinting : hinting ;
style = style = = - 1 ? font - > style : style ;
return ren_font_load ( font - > path , size , antialiasing , hinting , style ) ;
}
const char * ren_font_get_path ( RenFont * font ) {
return font - > path ;
2021-09-11 04:22:30 +02:00
}
2019-12-28 12:16:32 +01:00
2021-09-11 04:22:30 +02:00
void ren_font_free ( RenFont * font ) {
2022-06-22 19:19:52 +02:00
font_clear_glyph_cache ( font ) ;
2021-09-11 04:22:30 +02:00
FT_Done_Face ( font - > face ) ;
2022-11-14 15:01:24 +01:00
# ifdef _WIN32
free ( font - > file ) ;
2022-11-17 06:09:38 +01:00
CloseHandle ( font - > file_handle ) ;
2022-11-14 15:01:24 +01:00
# endif
2020-05-08 14:41:39 +02:00
free ( font ) ;
2019-12-28 12:16:32 +01:00
}
2021-10-13 05:24:52 +02:00
void ren_font_group_set_tab_size ( RenFont * * fonts , int n ) {
for ( int j = 0 ; j < FONT_FALLBACK_MAX & & fonts [ j ] ; + + j ) {
2022-06-12 02:22:01 +02:00
for ( int i = 0 ; i < ( fonts [ j ] - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? SUBPIXEL_BITMAPS_CACHED : 1 ) ; + + i )
2021-10-13 05:24:52 +02:00
font_get_glyphset ( fonts [ j ] , ' \t ' , i ) - > metrics [ ' \t ' ] . xadvance = fonts [ j ] - > space_advance * n ;
}
2019-12-28 12:16:32 +01:00
}
2021-10-13 05:24:52 +02:00
int ren_font_group_get_tab_size ( RenFont * * fonts ) {
2022-04-26 04:33:46 +02:00
float advance = font_get_glyphset ( fonts [ 0 ] , ' \t ' , 0 ) - > metrics [ ' \t ' ] . xadvance ;
2022-04-15 17:34:46 +02:00
if ( fonts [ 0 ] - > space_advance ) {
advance / = fonts [ 0 ] - > space_advance ;
}
return advance ;
2020-09-05 16:06:12 +02:00
}
2021-10-17 05:49:42 +02:00
float ren_font_group_get_size ( RenFont * * fonts ) {
return fonts [ 0 ] - > size ;
}
2022-06-22 19:19:52 +02:00
void ren_font_group_set_size ( RenFont * * fonts , float size ) {
const int surface_scale = renwin_surface_scale ( & window_renderer ) ;
for ( int i = 0 ; i < FONT_FALLBACK_MAX & & fonts [ i ] ; + + i ) {
font_clear_glyph_cache ( fonts [ i ] ) ;
FT_Face face = fonts [ i ] - > face ;
FT_Set_Pixel_Sizes ( face , 0 , ( int ) ( size * surface_scale ) ) ;
fonts [ i ] - > size = size ;
fonts [ i ] - > height = ( short ) ( ( face - > height / ( float ) face - > units_per_EM ) * size ) ;
fonts [ i ] - > baseline = ( short ) ( ( face - > ascender / ( float ) face - > units_per_EM ) * size ) ;
FT_Load_Char ( face , ' ' , font_set_load_options ( fonts [ i ] ) ) ;
fonts [ i ] - > space_advance = face - > glyph - > advance . x / 64.0f ;
fonts [ i ] - > tab_advance = fonts [ i ] - > space_advance * 2 ;
}
}
2021-10-17 05:49:42 +02:00
int ren_font_group_get_height ( RenFont * * fonts ) {
2022-02-15 21:57:07 +01:00
return fonts [ 0 ] - > height ;
2021-10-17 05:49:42 +02:00
}
2022-12-26 18:49:07 +01:00
float ren_font_group_get_width ( RenFont * * fonts , const char * text , size_t len ) {
2021-09-25 05:34:19 +02:00
float width = 0 ;
2022-12-26 18:49:07 +01:00
const char * end = text + len ;
2021-10-17 06:10:40 +02:00
GlyphMetric * metric = NULL ; GlyphSet * set = NULL ;
2021-09-11 04:22:30 +02:00
while ( text < end ) {
unsigned int codepoint ;
text = utf8_to_codepoint ( text , & codepoint ) ;
2022-02-15 21:57:07 +01:00
RenFont * font = font_group_get_glyph ( & set , & metric , fonts , codepoint , 0 ) ;
2022-04-15 17:34:46 +02:00
if ( ! metric )
break ;
2022-02-15 21:57:07 +01:00
width + = ( ! font | | metric - > xadvance ) ? metric - > xadvance : fonts [ 0 ] - > space_advance ;
2019-12-28 12:16:32 +01:00
}
2021-09-22 03:49:32 +02:00
const int surface_scale = renwin_surface_scale ( & window_renderer ) ;
return width / surface_scale ;
2019-12-28 12:16:32 +01:00
}
2022-12-26 18:49:07 +01:00
float ren_draw_text ( RenFont * * fonts , const char * text , size_t len , float x , int y , RenColor color ) {
2021-09-11 04:22:30 +02:00
SDL_Surface * surface = renwin_get_surface ( & window_renderer ) ;
const RenRect clip = window_renderer . clip ;
2021-09-22 03:49:32 +02:00
const int surface_scale = renwin_surface_scale ( & window_renderer ) ;
float pen_x = x * surface_scale ;
y * = surface_scale ;
2021-09-11 04:22:30 +02:00
int bytes_per_pixel = surface - > format - > BytesPerPixel ;
2022-12-26 18:49:07 +01:00
const char * end = text + len ;
2022-04-15 17:34:46 +02:00
uint8_t * destination_pixels = surface - > pixels ;
2021-09-11 04:22:30 +02:00
int clip_end_x = clip . x + clip . width , clip_end_y = clip . y + clip . height ;
2022-06-12 02:22:01 +02:00
2022-11-27 00:45:57 +01:00
RenFont * last = NULL ;
2022-08-20 22:15:08 +02:00
float last_pen_x = x ;
bool underline = fonts [ 0 ] - > style & FONT_STYLE_UNDERLINE ;
bool strikethrough = fonts [ 0 ] - > style & FONT_STYLE_STRIKETHROUGH ;
2021-09-11 04:22:30 +02:00
while ( text < end ) {
unsigned int codepoint , r , g , b ;
text = utf8_to_codepoint ( text , & codepoint ) ;
2022-06-12 02:22:01 +02:00
GlyphSet * set = NULL ; GlyphMetric * metric = NULL ;
2021-10-17 06:10:40 +02:00
RenFont * font = font_group_get_glyph ( & set , & metric , fonts , codepoint , ( int ) ( fmod ( pen_x , 1.0 ) * SUBPIXEL_BITMAPS_CACHED ) ) ;
2022-04-15 17:34:46 +02:00
if ( ! metric )
break ;
2021-10-08 00:54:23 +02:00
int start_x = floor ( pen_x ) + metric - > bitmap_left ;
int end_x = ( metric - > x1 - metric - > x0 ) + start_x ;
2021-09-17 00:41:54 +02:00
int glyph_end = metric - > x1 , glyph_start = metric - > x0 ;
2021-10-17 06:22:27 +02:00
if ( ! metric - > loaded & & codepoint > 0xFF )
ren_draw_rect ( ( RenRect ) { start_x + 1 , y , font - > space_advance - 1 , ren_font_group_get_height ( fonts ) } , color ) ;
2021-10-02 05:56:36 +02:00
if ( set - > surface & & color . a > 0 & & end_x > = clip . x & & start_x < clip_end_x ) {
2022-04-15 17:34:46 +02:00
uint8_t * source_pixels = set - > surface - > pixels ;
2021-09-11 04:22:30 +02:00
for ( int line = metric - > y0 ; line < metric - > y1 ; + + line ) {
2022-02-15 21:57:07 +01:00
int target_y = line + y - metric - > bitmap_top + font - > baseline * surface_scale ;
2021-09-17 00:41:54 +02:00
if ( target_y < clip . y )
2021-09-11 04:22:30 +02:00
continue ;
2021-09-17 00:41:54 +02:00
if ( target_y > = clip_end_y )
2021-09-11 04:22:30 +02:00
break ;
2021-09-25 06:45:19 +02:00
if ( start_x + ( glyph_end - glyph_start ) > = clip_end_x )
glyph_end = glyph_start + ( clip_end_x - start_x ) ;
2022-03-07 23:35:56 +01:00
if ( start_x < clip . x ) {
2021-10-13 03:22:02 +02:00
int offset = clip . x - start_x ;
start_x + = offset ;
glyph_start + = offset ;
}
2022-04-15 17:34:46 +02:00
uint32_t * destination_pixel = ( uint32_t * ) & ( destination_pixels [ surface - > pitch * target_y + start_x * bytes_per_pixel ] ) ;
uint8_t * source_pixel = & source_pixels [ line * set - > surface - > pitch + glyph_start * ( font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ? 3 : 1 ) ] ;
2021-09-17 00:41:54 +02:00
for ( int x = glyph_start ; x < glyph_end ; + + x ) {
2022-04-15 17:34:46 +02:00
uint32_t destination_color = * destination_pixel ;
// the standard way of doing this would be SDL_GetRGBA, but that introduces a performance regression. needs to be investigated
2021-10-21 00:43:22 +02:00
SDL_Color dst = { ( destination_color & surface - > format - > Rmask ) > > surface - > format - > Rshift , ( destination_color & surface - > format - > Gmask ) > > surface - > format - > Gshift , ( destination_color & surface - > format - > Bmask ) > > surface - > format - > Bshift , ( destination_color & surface - > format - > Amask ) > > surface - > format - > Ashift } ;
2022-04-15 17:34:46 +02:00
SDL_Color src ;
if ( font - > antialiasing = = FONT_ANTIALIASING_SUBPIXEL ) {
src . r = * ( source_pixel + + ) ;
src . g = * ( source_pixel + + ) ;
}
else {
src . r = * ( source_pixel ) ;
src . g = * ( source_pixel ) ;
}
src . b = * ( source_pixel + + ) ;
src . a = 0xFF ;
2021-10-17 04:59:41 +02:00
r = ( color . r * src . r * color . a + dst . r * ( 65025 - src . r * color . a ) + 32767 ) / 65025 ;
g = ( color . g * src . g * color . a + dst . g * ( 65025 - src . g * color . a ) + 32767 ) / 65025 ;
b = ( color . b * src . b * color . a + dst . b * ( 65025 - src . b * color . a ) + 32767 ) / 65025 ;
2022-04-15 17:34:46 +02:00
// the standard way of doing this would be SDL_GetRGBA, but that introduces a performance regression. needs to be investigated
2021-10-21 00:43:22 +02:00
* destination_pixel + + = dst . a < < surface - > format - > Ashift | r < < surface - > format - > Rshift | g < < surface - > format - > Gshift | b < < surface - > format - > Bshift ;
2021-09-11 04:22:30 +02:00
}
}
}
2022-08-20 22:15:08 +02:00
float adv = metric - > xadvance ? metric - > xadvance : font - > space_advance ;
if ( ! last ) last = font ;
else if ( font ! = last | | text = = end ) {
float local_pen_x = text = = end ? pen_x + adv : pen_x ;
if ( underline )
ren_draw_rect ( ( RenRect ) { last_pen_x , y / surface_scale + last - > height - 1 , ( local_pen_x - last_pen_x ) / surface_scale , last - > underline_thickness * surface_scale } , color ) ;
if ( strikethrough )
ren_draw_rect ( ( RenRect ) { last_pen_x , y / surface_scale + last - > height / 2 , ( local_pen_x - last_pen_x ) / surface_scale , last - > underline_thickness * surface_scale } , color ) ;
last = font ;
last_pen_x = pen_x ;
}
pen_x + = adv ;
2021-09-11 04:22:30 +02:00
}
2021-09-22 03:49:32 +02:00
return pen_x / surface_scale ;
2021-09-11 04:22:30 +02:00
}
2019-12-28 12:16:32 +01:00
2021-09-11 04:22:30 +02:00
/******************* Rectangles **********************/
2019-12-28 12:16:32 +01:00
static inline RenColor blend_pixel ( RenColor dst , RenColor src ) {
int ia = 0xff - src . a ;
dst . r = ( ( src . r * src . a ) + ( dst . r * ia ) ) > > 8 ;
dst . g = ( ( src . g * src . a ) + ( dst . g * ia ) ) > > 8 ;
dst . b = ( ( src . b * src . a ) + ( dst . b * ia ) ) > > 8 ;
return dst ;
}
void ren_draw_rect ( RenRect rect , RenColor color ) {
if ( color . a = = 0 ) { return ; }
2021-04-29 14:15:24 +02:00
const int surface_scale = renwin_surface_scale ( & window_renderer ) ;
2021-04-24 10:21:34 +02:00
/* transforms coordinates in pixels. */
rect . x * = surface_scale ;
rect . y * = surface_scale ;
rect . width * = surface_scale ;
rect . height * = surface_scale ;
2021-04-29 14:15:24 +02:00
const RenRect clip = window_renderer . clip ;
int x1 = rect . x < clip . x ? clip . x : rect . x ;
int y1 = rect . y < clip . y ? clip . y : rect . y ;
2019-12-28 12:16:32 +01:00
int x2 = rect . x + rect . width ;
int y2 = rect . y + rect . height ;
2021-04-29 14:15:24 +02:00
x2 = x2 > clip . x + clip . width ? clip . x + clip . width : x2 ;
y2 = y2 > clip . y + clip . height ? clip . y + clip . height : y2 ;
2019-12-28 12:16:32 +01:00
2021-04-29 14:15:24 +02:00
SDL_Surface * surface = renwin_get_surface ( & window_renderer ) ;
2022-04-20 23:13:42 +02:00
SDL_Rect dest_rect = { x1 , y1 , x2 - x1 , y2 - y1 } ;
2019-12-28 12:16:32 +01:00
if ( color . a = = 0xff ) {
2022-02-03 01:50:43 +01:00
uint32_t translated = SDL_MapRGB ( surface - > format , color . r , color . g , color . b ) ;
2022-04-20 23:13:42 +02:00
SDL_FillRect ( surface , & dest_rect , translated ) ;
2019-12-28 12:16:32 +01:00
} else {
2022-04-20 23:13:42 +02:00
uint32_t * pixel = ( uint32_t * ) draw_rect_surface - > pixels ;
* pixel = SDL_MapRGBA ( draw_rect_surface - > format , color . r , color . g , color . b , color . a ) ;
SDL_BlitScaled ( draw_rect_surface , NULL , surface , & dest_rect ) ;
2019-12-28 12:16:32 +01:00
}
}
2021-09-11 04:22:30 +02:00
/*************** Window Management ****************/
void ren_free_window_resources ( ) {
renwin_free ( & window_renderer ) ;
2022-04-20 23:13:42 +02:00
SDL_FreeSurface ( draw_rect_surface ) ;
2021-09-11 04:22:30 +02:00
}
2019-12-28 12:16:32 +01:00
2021-09-11 04:22:30 +02:00
void ren_init ( SDL_Window * win ) {
assert ( win ) ;
int error = FT_Init_FreeType ( & library ) ;
if ( error ) {
fprintf ( stderr , " internal font error when starting the application \n " ) ;
return ;
2021-03-31 17:33:35 +02:00
}
2021-09-11 04:22:30 +02:00
window_renderer . window = win ;
renwin_init_surface ( & window_renderer ) ;
renwin_clip_to_surface ( & window_renderer ) ;
2022-04-20 23:13:42 +02:00
draw_rect_surface = SDL_CreateRGBSurface ( 0 , 1 , 1 , 32 ,
0xFF000000 , 0x00FF0000 , 0x0000FF00 , 0x000000FF ) ;
2021-03-31 17:33:35 +02:00
}
2021-04-29 14:15:24 +02:00
2021-09-11 04:22:30 +02:00
void ren_resize_window ( ) {
renwin_resize_surface ( & window_renderer ) ;
2021-04-29 14:15:24 +02:00
}
2021-09-11 04:22:30 +02:00
void ren_update_rects ( RenRect * rects , int count ) {
static bool initial_frame = true ;
if ( initial_frame ) {
renwin_show_window ( & window_renderer ) ;
initial_frame = false ;
2019-12-28 12:16:32 +01:00
}
2021-09-11 04:22:30 +02:00
renwin_update_rects ( & window_renderer , rects , count ) ;
2021-03-06 16:18:24 +01:00
}
2021-04-24 10:21:34 +02:00
2021-09-11 04:22:30 +02:00
void ren_set_clip_rect ( RenRect rect ) {
renwin_set_clip_rect ( & window_renderer , rect ) ;
2021-04-24 10:21:34 +02:00
}
2021-03-06 18:12:02 +01:00
2021-09-11 04:22:30 +02:00
void ren_get_size ( int * x , int * y ) {
RenWindow * ren = & window_renderer ;
const int scale = renwin_surface_scale ( ren ) ;
SDL_Surface * surface = renwin_get_surface ( ren ) ;
* x = surface - > w / scale ;
* y = surface - > h / scale ;
2021-03-06 16:18:24 +01:00
}