2016-06-18 14:46:12 +02:00
/*************************************************************************/
/* dynamic_font.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2016-06-18 14:46:12 +02:00
/*************************************************************************/
2021-01-01 20:13:46 +01:00
/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
2016-06-18 14:46:12 +02:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2020-02-06 21:51:36 +01:00
# include "modules/modules_enabled.gen.h"
# ifdef MODULE_FREETYPE_ENABLED
2016-05-02 04:12:30 +02:00
# include "dynamic_font.h"
2020-02-06 21:51:36 +01:00
2018-09-11 18:13:45 +02:00
# include "core/os/file_access.h"
# include "core/os/os.h"
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
# include FT_STROKER_H
# define __STDC_LIMIT_MACROS
# include <stdint.h>
2016-08-02 16:00:32 +02:00
2018-03-17 09:44:34 +01:00
bool DynamicFontData : : CacheID : : operator < ( CacheID right ) const {
2018-01-21 20:18:52 +01:00
return key < right . key ;
2016-08-02 16:00:32 +02:00
}
2016-05-02 04:12:30 +02:00
2017-08-11 21:10:05 +02:00
Ref < DynamicFontAtSize > DynamicFontData : : _get_dynamic_font_at_size ( CacheID p_cache_id ) {
if ( size_cache . has ( p_cache_id ) ) {
return Ref < DynamicFontAtSize > ( size_cache [ p_cache_id ] ) ;
2016-05-02 04:12:30 +02:00
}
Ref < DynamicFontAtSize > dfas ;
2016-05-29 16:37:26 +02:00
2016-05-02 04:12:30 +02:00
dfas . instance ( ) ;
2017-03-05 16:44:50 +01:00
dfas - > font = Ref < DynamicFontData > ( this ) ;
2016-05-02 04:12:30 +02:00
2017-08-11 21:10:05 +02:00
size_cache [ p_cache_id ] = dfas . ptr ( ) ;
dfas - > id = p_cache_id ;
2016-05-29 16:37:26 +02:00
dfas - > _load ( ) ;
return dfas ;
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
void DynamicFontData : : set_font_ptr ( const uint8_t * p_font_mem , int p_font_mem_size ) {
font_mem = p_font_mem ;
font_mem_size = p_font_mem_size ;
2016-05-29 16:37:26 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
void DynamicFontData : : set_font_path ( const String & p_path ) {
font_path = p_path ;
2016-05-29 16:37:26 +02:00
}
2016-06-19 00:03:53 +02:00
String DynamicFontData : : get_font_path ( ) const {
return font_path ;
}
2016-05-29 16:37:26 +02:00
void DynamicFontData : : set_force_autohinter ( bool p_force ) {
2017-03-05 16:44:50 +01:00
force_autohinter = p_force ;
2016-05-02 04:12:30 +02:00
}
2016-06-19 00:03:53 +02:00
void DynamicFontData : : _bind_methods ( ) {
2018-11-10 22:07:32 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_antialiased " , " antialiased " ) , & DynamicFontData : : set_antialiased ) ;
ClassDB : : bind_method ( D_METHOD ( " is_antialiased " ) , & DynamicFontData : : is_antialiased ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_font_path " , " path " ) , & DynamicFontData : : set_font_path ) ;
ClassDB : : bind_method ( D_METHOD ( " get_font_path " ) , & DynamicFontData : : get_font_path ) ;
2018-02-27 15:04:08 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_hinting " , " mode " ) , & DynamicFontData : : set_hinting ) ;
ClassDB : : bind_method ( D_METHOD ( " get_hinting " ) , & DynamicFontData : : get_hinting ) ;
2018-11-10 22:07:32 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " antialiased " ) , " set_antialiased " , " is_antialiased " ) ;
2018-02-27 15:04:08 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " hinting " , PROPERTY_HINT_ENUM , " None,Light,Normal " ) , " set_hinting " , " get_hinting " ) ;
BIND_ENUM_CONSTANT ( HINTING_NONE ) ;
BIND_ENUM_CONSTANT ( HINTING_LIGHT ) ;
BIND_ENUM_CONSTANT ( HINTING_NORMAL ) ;
2016-06-19 00:03:53 +02:00
2021-08-24 04:29:18 +02:00
// Only WOFF1 is supported as WOFF2 requires a Brotli decompression library to be linked.
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " font_path " , PROPERTY_HINT_FILE , " *.ttf,*.otf,*.woff " ) , " set_font_path " , " get_font_path " ) ;
2016-06-19 00:03:53 +02:00
}
2017-03-05 16:44:50 +01:00
DynamicFontData : : DynamicFontData ( ) {
2018-11-10 22:07:32 +01:00
antialiased = true ;
2017-03-05 16:44:50 +01:00
force_autohinter = false ;
2018-02-27 15:04:08 +01:00
hinting = DynamicFontData : : HINTING_NORMAL ;
2021-05-04 16:00:45 +02:00
font_mem = nullptr ;
2017-03-05 16:44:50 +01:00
font_mem_size = 0 ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
DynamicFontData : : ~ DynamicFontData ( ) {
2016-05-02 04:12:30 +02:00
}
////////////////////
2016-05-29 16:37:26 +02:00
Error DynamicFontAtSize : : _load ( ) {
2017-03-05 16:44:50 +01:00
int error = FT_Init_FreeType ( & library ) ;
2016-05-29 16:37:26 +02:00
2019-08-08 22:11:48 +02:00
ERR_FAIL_COND_V_MSG ( error ! = 0 , ERR_CANT_CREATE , " Error initializing FreeType. " ) ;
2016-05-29 16:37:26 +02:00
2021-05-04 16:00:45 +02:00
if ( font - > font_mem = = nullptr & & font - > font_path ! = String ( ) ) {
2017-03-05 16:44:50 +01:00
FileAccess * f = FileAccess : : open ( font - > font_path , FileAccess : : READ ) ;
2020-01-20 04:00:51 +01:00
if ( ! f ) {
FT_Done_FreeType ( library ) ;
ERR_FAIL_V_MSG ( ERR_CANT_OPEN , " Cannot open font file ' " + font - > font_path + " '. " ) ;
}
2016-05-29 16:37:26 +02:00
2019-03-26 18:51:13 +01:00
uint64_t len = f - > get_len ( ) ;
2020-12-07 09:24:30 +01:00
font - > _fontdata = Vector < uint8_t > ( ) ;
font - > _fontdata . resize ( len ) ;
f - > get_buffer ( font - > _fontdata . ptrw ( ) , len ) ;
font - > set_font_ptr ( font - > _fontdata . ptr ( ) , len ) ;
f - > close ( ) ;
2021-01-17 17:53:13 +01:00
memdelete ( f ) ;
2020-12-07 09:24:30 +01:00
}
2016-05-29 16:37:26 +02:00
2020-12-07 09:24:30 +01:00
if ( font - > font_mem ) {
2017-03-05 16:44:50 +01:00
memset ( & stream , 0 , sizeof ( FT_StreamRec ) ) ;
stream . base = ( unsigned char * ) font - > font_mem ;
stream . size = font - > font_mem_size ;
stream . pos = 0 ;
2016-05-29 16:37:26 +02:00
FT_Open_Args fargs ;
2017-03-05 16:44:50 +01:00
memset ( & fargs , 0 , sizeof ( FT_Open_Args ) ) ;
fargs . memory_base = ( unsigned char * ) font - > font_mem ;
fargs . memory_size = font - > font_mem_size ;
fargs . flags = FT_OPEN_MEMORY ;
fargs . stream = & stream ;
error = FT_Open_Face ( library , & fargs , 0 , & face ) ;
2016-05-29 16:37:26 +02:00
} else {
2020-01-20 04:00:51 +01:00
FT_Done_FreeType ( library ) ;
2019-08-08 22:11:48 +02:00
ERR_FAIL_V_MSG ( ERR_UNCONFIGURED , " DynamicFont uninitialized. " ) ;
2016-05-29 16:37:26 +02:00
}
//error = FT_New_Face( library, src_path.utf8().get_data(),0,&face );
2017-03-05 16:44:50 +01:00
if ( error = = FT_Err_Unknown_File_Format ) {
FT_Done_FreeType ( library ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CANT_OPEN , " Unknown font format. " ) ;
2016-05-29 16:37:26 +02:00
2017-03-05 16:44:50 +01:00
} else if ( error ) {
FT_Done_FreeType ( library ) ;
2019-09-25 10:28:50 +02:00
ERR_FAIL_V_MSG ( ERR_FILE_CANT_OPEN , " Error loading font. " ) ;
2016-05-29 16:37:26 +02:00
}
2019-09-29 17:05:11 +02:00
if ( FT_HAS_COLOR ( face ) & & face - > num_fixed_sizes > 0 ) {
2018-01-16 14:22:59 +01:00
int best_match = 0 ;
2018-10-04 15:38:52 +02:00
int diff = ABS ( id . size - ( ( int64_t ) face - > available_sizes [ 0 ] . width ) ) ;
2020-01-06 18:15:23 +01:00
scale_color_font = float ( id . size * oversampling ) / face - > available_sizes [ 0 ] . width ;
2018-01-16 14:22:59 +01:00
for ( int i = 1 ; i < face - > num_fixed_sizes ; i + + ) {
2018-10-04 15:38:52 +02:00
int ndiff = ABS ( id . size - ( ( int64_t ) face - > available_sizes [ i ] . width ) ) ;
2018-01-16 14:22:59 +01:00
if ( ndiff < diff ) {
best_match = i ;
diff = ndiff ;
2020-01-06 18:15:23 +01:00
scale_color_font = float ( id . size * oversampling ) / face - > available_sizes [ i ] . width ;
2018-01-16 14:22:59 +01:00
}
}
2018-03-26 01:36:34 +02:00
FT_Select_Size ( face , best_match ) ;
2018-01-16 14:22:59 +01:00
} else {
2018-03-26 01:36:34 +02:00
FT_Set_Pixel_Sizes ( face , 0 , id . size * oversampling ) ;
2018-01-16 14:22:59 +01:00
}
2016-05-29 16:37:26 +02:00
2018-03-17 09:44:34 +01:00
ascent = ( face - > size - > metrics . ascender / 64.0 ) / oversampling * scale_color_font ;
descent = ( - face - > size - > metrics . descender / 64.0 ) / oversampling * scale_color_font ;
2017-03-05 16:44:50 +01:00
linegap = 0 ;
texture_flags = 0 ;
2021-05-05 12:44:11 +02:00
if ( id . mipmaps ) {
2017-03-05 16:44:50 +01:00
texture_flags | = Texture : : FLAG_MIPMAPS ;
2021-05-05 12:44:11 +02:00
}
if ( id . filter ) {
2017-03-05 16:44:50 +01:00
texture_flags | = Texture : : FLAG_FILTER ;
2021-05-05 12:44:11 +02:00
}
2016-05-29 16:37:26 +02:00
2017-03-05 16:44:50 +01:00
valid = true ;
2016-05-29 16:37:26 +02:00
return OK ;
}
2017-12-19 22:48:30 +01:00
float DynamicFontAtSize : : font_oversampling = 1.0 ;
2016-05-02 04:12:30 +02:00
float DynamicFontAtSize : : get_height ( ) const {
2017-03-05 16:44:50 +01:00
return ascent + descent ;
2016-05-02 04:12:30 +02:00
}
float DynamicFontAtSize : : get_ascent ( ) const {
2016-05-29 16:37:26 +02:00
return ascent ;
2016-05-02 04:12:30 +02:00
}
float DynamicFontAtSize : : get_descent ( ) const {
2016-05-29 16:37:26 +02:00
return descent ;
2016-05-02 04:12:30 +02:00
}
2021-05-04 14:20:36 +02:00
const Pair < const DynamicFontAtSize : : Character * , DynamicFontAtSize * > DynamicFontAtSize : : _find_char_with_font ( CharType p_char , const Vector < Ref < DynamicFontAtSize > > & p_fallbacks ) const {
2018-03-17 09:44:34 +01:00
const Character * chr = char_map . getptr ( p_char ) ;
ERR_FAIL_COND_V ( ! chr , ( Pair < const Character * , DynamicFontAtSize * > ( NULL , NULL ) ) ) ;
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
if ( ! chr - > found ) {
2016-05-31 00:41:32 +02:00
//not found, try in fallbacks
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < p_fallbacks . size ( ) ; i + + ) {
DynamicFontAtSize * fb = const_cast < DynamicFontAtSize * > ( p_fallbacks [ i ] . ptr ( ) ) ;
2021-05-05 12:44:11 +02:00
if ( ! fb - > valid ) {
2016-05-31 00:41:32 +02:00
continue ;
2021-05-05 12:44:11 +02:00
}
2016-05-31 00:41:32 +02:00
fb - > _update_char ( p_char ) ;
2018-03-17 09:44:34 +01:00
const Character * fallback_chr = fb - > char_map . getptr ( p_char ) ;
ERR_CONTINUE ( ! fallback_chr ) ;
2016-05-31 00:41:32 +02:00
2021-05-05 12:44:11 +02:00
if ( ! fallback_chr - > found ) {
2016-05-31 00:41:32 +02:00
continue ;
2021-05-05 12:44:11 +02:00
}
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
return Pair < const Character * , DynamicFontAtSize * > ( fallback_chr , fb ) ;
2016-05-31 00:41:32 +02:00
}
2018-03-17 09:44:34 +01:00
//not found, try 0xFFFD to display 'not found'.
const_cast < DynamicFontAtSize * > ( this ) - > _update_char ( 0xFFFD ) ;
chr = char_map . getptr ( 0xFFFD ) ;
ERR_FAIL_COND_V ( ! chr , ( Pair < const Character * , DynamicFontAtSize * > ( NULL , NULL ) ) ) ;
2016-05-31 00:41:32 +02:00
}
2018-03-17 09:44:34 +01:00
return Pair < const Character * , DynamicFontAtSize * > ( chr , const_cast < DynamicFontAtSize * > ( this ) ) ;
}
2021-06-07 02:36:10 +02:00
float DynamicFontAtSize : : _get_kerning_advance ( const DynamicFontAtSize * font , CharType p_char , CharType p_next ) const {
float advance = 0.0 ;
if ( p_next ) {
FT_Vector delta ;
FT_Get_Kerning ( font - > face , FT_Get_Char_Index ( font - > face , p_char ) , FT_Get_Char_Index ( font - > face , p_next ) , FT_KERNING_DEFAULT , & delta ) ;
advance = ( delta . x / 64.0 ) / oversampling ;
}
return advance ;
}
2021-05-04 14:20:36 +02:00
Size2 DynamicFontAtSize : : get_char_size ( CharType p_char , CharType p_next , const Vector < Ref < DynamicFontAtSize > > & p_fallbacks ) const {
2021-05-05 12:44:11 +02:00
if ( ! valid ) {
2018-03-17 09:44:34 +01:00
return Size2 ( 1 , 1 ) ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
const_cast < DynamicFontAtSize * > ( this ) - > _update_char ( p_char ) ;
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
Pair < const Character * , DynamicFontAtSize * > char_pair_with_font = _find_char_with_font ( p_char , p_fallbacks ) ;
const Character * ch = char_pair_with_font . first ;
2021-06-07 02:36:10 +02:00
DynamicFontAtSize * font = char_pair_with_font . second ;
2018-03-17 09:44:34 +01:00
ERR_FAIL_COND_V ( ! ch , Size2 ( ) ) ;
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
Size2 ret ( 0 , get_height ( ) ) ;
if ( ch - > found ) {
ret . x = ch - > advance ;
2016-05-02 04:12:30 +02:00
}
2021-06-07 02:36:10 +02:00
ret . x + = _get_kerning_advance ( font , p_char , p_next ) ;
2018-03-14 13:38:34 +01:00
2016-05-02 04:12:30 +02:00
return ret ;
}
2020-07-18 14:53:37 +02:00
String DynamicFontAtSize : : get_available_chars ( ) const {
2021-05-21 04:22:20 +02:00
if ( ! valid ) {
return " " ;
}
2020-07-18 14:53:37 +02:00
String chars ;
FT_UInt gindex ;
FT_ULong charcode = FT_Get_First_Char ( face , & gindex ) ;
while ( gindex ! = 0 ) {
if ( charcode ! = 0 ) {
chars + = CharType ( charcode ) ;
}
charcode = FT_Get_Next_Char ( face , charcode , & gindex ) ;
}
return chars ;
}
2017-03-05 16:44:50 +01:00
void DynamicFontAtSize : : set_texture_flags ( uint32_t p_flags ) {
texture_flags = p_flags ;
for ( int i = 0 ; i < textures . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
Ref < ImageTexture > & tex = textures . write [ i ] . texture ;
2021-05-05 12:44:11 +02:00
if ( ! tex . is_null ( ) ) {
2016-08-02 09:01:51 +02:00
tex - > set_flags ( p_flags ) ;
2021-05-05 12:44:11 +02:00
}
2016-08-02 09:01:51 +02:00
}
}
2016-05-02 04:12:30 +02:00
2021-05-04 14:20:36 +02:00
float DynamicFontAtSize : : draw_char ( RID p_canvas_item , const Point2 & p_pos , CharType p_char , CharType p_next , const Color & p_modulate , const Vector < Ref < DynamicFontAtSize > > & p_fallbacks , bool p_advance_only , bool p_outline ) const {
2021-05-05 12:44:11 +02:00
if ( ! valid ) {
2016-05-29 16:37:26 +02:00
return 0 ;
2021-05-05 12:44:11 +02:00
}
2016-05-29 16:37:26 +02:00
2017-03-05 16:44:50 +01:00
const_cast < DynamicFontAtSize * > ( this ) - > _update_char ( p_char ) ;
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
Pair < const Character * , DynamicFontAtSize * > char_pair_with_font = _find_char_with_font ( p_char , p_fallbacks ) ;
const Character * ch = char_pair_with_font . first ;
DynamicFontAtSize * font = char_pair_with_font . second ;
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
ERR_FAIL_COND_V ( ! ch , 0.0 ) ;
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
float advance = 0.0 ;
2016-05-31 00:41:32 +02:00
2020-01-15 00:28:53 +01:00
// use normal character size if there's no outline character
2020-01-06 18:15:23 +01:00
if ( p_outline & & ! ch - > found ) {
FT_GlyphSlot slot = face - > glyph ;
int error = FT_Load_Char ( face , p_char , FT_HAS_COLOR ( face ) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT ) ;
if ( ! error ) {
error = FT_Render_Glyph ( face - > glyph , FT_RENDER_MODE_NORMAL ) ;
if ( ! error ) {
Character character = Character : : not_found ( ) ;
character = const_cast < DynamicFontAtSize * > ( this ) - > _bitmap_to_character ( slot - > bitmap , slot - > bitmap_top , slot - > bitmap_left , slot - > advance . x / 64.0 ) ;
advance = character . advance ;
}
}
}
2018-03-17 09:44:34 +01:00
if ( ch - > found ) {
ERR_FAIL_COND_V ( ch - > texture_idx < - 1 | | ch - > texture_idx > = font - > textures . size ( ) , 0 ) ;
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
if ( ! p_advance_only & & ch - > texture_idx ! = - 1 ) {
2017-03-05 16:44:50 +01:00
Point2 cpos = p_pos ;
cpos . x + = ch - > h_align ;
2018-03-17 09:44:34 +01:00
cpos . y - = font - > get_ascent ( ) ;
2017-03-05 16:44:50 +01:00
cpos . y + = ch - > v_align ;
2018-01-16 14:22:59 +01:00
Color modulate = p_modulate ;
2020-12-09 10:20:55 +01:00
if ( FT_HAS_COLOR ( font - > face ) ) {
2018-03-17 09:44:34 +01:00
modulate . r = modulate . g = modulate . b = 1.0 ;
2018-01-16 14:22:59 +01:00
}
2018-03-17 09:44:34 +01:00
RID texture = font - > textures [ ch - > texture_idx ] . texture - > get_rid ( ) ;
2018-12-18 19:54:51 +01:00
VisualServer : : get_singleton ( ) - > canvas_item_add_texture_rect_region ( p_canvas_item , Rect2 ( cpos , ch - > rect . size ) , texture , ch - > rect_uv , modulate , false , RID ( ) , false ) ;
2018-01-16 14:22:59 +01:00
}
2016-05-31 00:41:32 +02:00
2018-03-17 09:44:34 +01:00
advance = ch - > advance ;
2016-05-02 04:12:30 +02:00
}
2021-06-07 02:36:10 +02:00
advance + = _get_kerning_advance ( font , p_char , p_next ) ;
2016-05-31 00:41:32 +02:00
return advance ;
2016-05-02 04:12:30 +02:00
}
2018-03-17 09:44:34 +01:00
DynamicFontAtSize : : Character DynamicFontAtSize : : Character : : not_found ( ) {
Character ch ;
ch . texture_idx = - 1 ;
ch . advance = 0 ;
ch . h_align = 0 ;
ch . v_align = 0 ;
ch . found = false ;
return ch ;
}
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
DynamicFontAtSize : : TexturePosition DynamicFontAtSize : : _find_texture_pos_for_glyph ( int p_color_size , Image : : Format p_image_format , int p_width , int p_height ) {
TexturePosition ret ;
ret . index = - 1 ;
ret . x = 0 ;
ret . y = 0 ;
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
int mw = p_width ;
int mh = p_height ;
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < textures . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
const CharTexture & ct = textures [ i ] ;
2016-05-02 04:12:30 +02:00
2021-05-05 12:44:11 +02:00
if ( ct . texture - > get_format ( ) ! = p_image_format ) {
2018-01-16 14:22:59 +01:00
continue ;
2021-05-05 12:44:11 +02:00
}
2018-01-16 14:22:59 +01:00
2021-05-05 12:44:11 +02:00
if ( mw > ct . texture_size | | mh > ct . texture_size ) { //too big for this texture
2016-05-02 04:12:30 +02:00
continue ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
ret . y = 0x7FFFFFFF ;
ret . x = 0 ;
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
for ( int j = 0 ; j < ct . texture_size - mw ; j + + ) {
int max_y = 0 ;
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
for ( int k = j ; k < j + mw ; k + + ) {
2016-05-02 04:12:30 +02:00
int y = ct . offsets [ k ] ;
2021-05-05 12:44:11 +02:00
if ( y > max_y ) {
2017-03-05 16:44:50 +01:00
max_y = y ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
}
2018-03-17 09:44:34 +01:00
if ( max_y < ret . y ) {
ret . y = max_y ;
ret . x = j ;
2016-05-02 04:12:30 +02:00
}
}
2021-05-05 12:44:11 +02:00
if ( ret . y = = 0x7FFFFFFF | | ret . y + mh > ct . texture_size ) {
2016-05-02 04:12:30 +02:00
continue ; //fail, could not fit it here
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
ret . index = i ;
2016-05-02 04:12:30 +02:00
break ;
}
2018-03-17 09:44:34 +01:00
if ( ret . index = = - 1 ) {
2016-05-02 04:12:30 +02:00
//could not find texture to fit, create one
2018-03-17 09:44:34 +01:00
ret . x = 0 ;
ret . y = 0 ;
2016-05-02 04:12:30 +02:00
2018-03-19 14:48:43 +01:00
int texsize = MAX ( id . size * oversampling * 8 , 256 ) ;
2021-05-05 12:44:11 +02:00
if ( mw > texsize ) {
2017-03-05 16:44:50 +01:00
texsize = mw ; //special case, adapt to it?
2021-05-05 12:44:11 +02:00
}
if ( mh > texsize ) {
2017-03-05 16:44:50 +01:00
texsize = mh ; //special case, adapt to it?
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2017-08-17 23:35:55 +02:00
texsize = next_power_of_2 ( texsize ) ;
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
texsize = MIN ( texsize , 4096 ) ;
2016-05-02 04:12:30 +02:00
CharTexture tex ;
2017-03-05 16:44:50 +01:00
tex . texture_size = texsize ;
2018-03-17 09:44:34 +01:00
tex . imgdata . resize ( texsize * texsize * p_color_size ) ; //grayscale alpha
2016-05-02 04:12:30 +02:00
{
//zero texture
2017-01-07 22:25:37 +01:00
PoolVector < uint8_t > : : Write w = tex . imgdata . write ( ) ;
2018-03-17 09:44:34 +01:00
ERR_FAIL_COND_V ( texsize * texsize * p_color_size > tex . imgdata . size ( ) , ret ) ;
2020-10-29 00:33:11 +01:00
// Initialize the texture to all-white pixels to prevent artifacts when the
// font is displayed at a non-default scale with filtering enabled.
if ( p_color_size = = 2 ) {
for ( int i = 0 ; i < texsize * texsize * p_color_size ; i + = 2 ) {
w [ i + 0 ] = 255 ;
w [ i + 1 ] = 0 ;
}
} else {
for ( int i = 0 ; i < texsize * texsize * p_color_size ; i + = 4 ) {
w [ i + 0 ] = 255 ;
w [ i + 1 ] = 255 ;
w [ i + 2 ] = 255 ;
w [ i + 3 ] = 0 ;
}
2016-05-02 04:12:30 +02:00
}
}
tex . offsets . resize ( texsize ) ;
2021-05-05 12:44:11 +02:00
for ( int i = 0 ; i < texsize ; i + + ) { //zero offsets
2018-07-25 03:11:03 +02:00
tex . offsets . write [ i ] = 0 ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
textures . push_back ( tex ) ;
2018-03-17 09:44:34 +01:00
ret . index = textures . size ( ) - 1 ;
2016-05-02 04:12:30 +02:00
}
2018-03-17 09:44:34 +01:00
return ret ;
}
DynamicFontAtSize : : Character DynamicFontAtSize : : _bitmap_to_character ( FT_Bitmap bitmap , int yofs , int xofs , float advance ) {
int w = bitmap . width ;
int h = bitmap . rows ;
int mw = w + rect_margin * 2 ;
int mh = h + rect_margin * 2 ;
ERR_FAIL_COND_V ( mw > 4096 , Character : : not_found ( ) ) ;
ERR_FAIL_COND_V ( mh > 4096 , Character : : not_found ( ) ) ;
int color_size = bitmap . pixel_mode = = FT_PIXEL_MODE_BGRA ? 4 : 2 ;
Image : : Format require_format = color_size = = 4 ? Image : : FORMAT_RGBA8 : Image : : FORMAT_LA8 ;
TexturePosition tex_pos = _find_texture_pos_for_glyph ( color_size , require_format , mw , mh ) ;
ERR_FAIL_COND_V ( tex_pos . index < 0 , Character : : not_found ( ) ) ;
2016-05-02 04:12:30 +02:00
//fit character in char texture
2018-07-25 03:11:03 +02:00
CharTexture & tex = textures . write [ tex_pos . index ] ;
2016-05-02 04:12:30 +02:00
{
2017-01-07 22:25:37 +01:00
PoolVector < uint8_t > : : Write wr = tex . imgdata . write ( ) ;
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < h ; i + + ) {
for ( int j = 0 ; j < w ; j + + ) {
2018-03-17 09:44:34 +01:00
int ofs = ( ( i + tex_pos . y + rect_margin ) * tex . texture_size + j + tex_pos . x + rect_margin ) * color_size ;
ERR_FAIL_COND_V ( ofs > = tex . imgdata . size ( ) , Character : : not_found ( ) ) ;
switch ( bitmap . pixel_mode ) {
2017-09-17 19:43:31 +02:00
case FT_PIXEL_MODE_MONO : {
2018-03-17 09:44:34 +01:00
int byte = i * bitmap . pitch + ( j > > 3 ) ;
2017-09-17 19:43:31 +02:00
int bit = 1 < < ( 7 - ( j % 8 ) ) ;
wr [ ofs + 0 ] = 255 ; //grayscale as 1
2019-08-07 12:54:30 +02:00
wr [ ofs + 1 ] = ( bitmap . buffer [ byte ] & bit ) ? 255 : 0 ;
2017-09-17 19:43:31 +02:00
} break ;
case FT_PIXEL_MODE_GRAY :
wr [ ofs + 0 ] = 255 ; //grayscale as 1
2018-03-17 09:44:34 +01:00
wr [ ofs + 1 ] = bitmap . buffer [ i * bitmap . pitch + j ] ;
2017-09-17 19:43:31 +02:00
break ;
2018-01-16 14:22:59 +01:00
case FT_PIXEL_MODE_BGRA : {
2018-03-17 09:44:34 +01:00
int ofs_color = i * bitmap . pitch + ( j < < 2 ) ;
wr [ ofs + 2 ] = bitmap . buffer [ ofs_color + 0 ] ;
wr [ ofs + 1 ] = bitmap . buffer [ ofs_color + 1 ] ;
wr [ ofs + 0 ] = bitmap . buffer [ ofs_color + 2 ] ;
wr [ ofs + 3 ] = bitmap . buffer [ ofs_color + 3 ] ;
2018-01-16 14:22:59 +01:00
} break ;
// TODO: FT_PIXEL_MODE_LCD
2017-09-17 19:43:31 +02:00
default :
2019-08-08 22:11:48 +02:00
ERR_FAIL_V_MSG ( Character : : not_found ( ) , " Font uses unsupported pixel format: " + itos ( bitmap . pixel_mode ) + " . " ) ;
2017-09-17 19:43:31 +02:00
break ;
}
2016-05-02 04:12:30 +02:00
}
}
}
//blit to image and texture
{
2018-01-16 14:22:59 +01:00
Ref < Image > img = memnew ( Image ( tex . texture_size , tex . texture_size , 0 , require_format , tex . imgdata ) ) ;
2016-05-02 04:12:30 +02:00
if ( tex . texture . is_null ( ) ) {
tex . texture . instance ( ) ;
2017-03-05 16:44:50 +01:00
tex . texture - > create_from_image ( img , Texture : : FLAG_VIDEO_SURFACE | texture_flags ) ;
2016-05-02 04:12:30 +02:00
} else {
tex . texture - > set_data ( img ) ; //update
}
}
// update height array
2018-03-17 09:44:34 +01:00
for ( int k = tex_pos . x ; k < tex_pos . x + mw ; k + + ) {
2018-07-25 03:11:03 +02:00
tex . offsets . write [ k ] = tex_pos . y + mh ;
2016-05-02 04:12:30 +02:00
}
Character chr ;
2018-01-16 14:22:59 +01:00
chr . h_align = xofs * scale_color_font / oversampling ;
chr . v_align = ascent - ( yofs * scale_color_font / oversampling ) ; // + ascent - descent;
chr . advance = advance * scale_color_font / oversampling ;
2018-03-17 09:44:34 +01:00
chr . texture_idx = tex_pos . index ;
2017-03-05 16:44:50 +01:00
chr . found = true ;
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
chr . rect_uv = Rect2 ( tex_pos . x + rect_margin , tex_pos . y + rect_margin , w , h ) ;
2017-12-19 22:48:30 +01:00
chr . rect = chr . rect_uv ;
chr . rect . position / = oversampling ;
2018-03-17 09:44:34 +01:00
chr . rect . size = chr . rect . size * scale_color_font / oversampling ;
return chr ;
}
DynamicFontAtSize : : Character DynamicFontAtSize : : _make_outline_char ( CharType p_char ) {
Character ret = Character : : not_found ( ) ;
2021-05-05 12:44:11 +02:00
if ( FT_Load_Char ( face , p_char , FT_LOAD_NO_BITMAP | ( font - > force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0 ) ) ! = 0 ) {
2018-03-17 09:44:34 +01:00
return ret ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
FT_Stroker stroker ;
2021-05-05 12:44:11 +02:00
if ( FT_Stroker_New ( library , & stroker ) ! = 0 ) {
2018-03-17 09:44:34 +01:00
return ret ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
FT_Stroker_Set ( stroker , ( int ) ( id . outline_size * oversampling * 64.0 ) , FT_STROKER_LINECAP_BUTT , FT_STROKER_LINEJOIN_ROUND , 0 ) ;
FT_Glyph glyph ;
FT_BitmapGlyph glyph_bitmap ;
2021-05-05 12:44:11 +02:00
if ( FT_Get_Glyph ( face - > glyph , & glyph ) ! = 0 ) {
2018-03-17 09:44:34 +01:00
goto cleanup_stroker ;
2021-05-05 12:44:11 +02:00
}
if ( FT_Glyph_Stroke ( & glyph , stroker , 1 ) ! = 0 ) {
2018-03-17 09:44:34 +01:00
goto cleanup_glyph ;
2021-05-05 12:44:11 +02:00
}
if ( FT_Glyph_To_Bitmap ( & glyph , font - > antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO , nullptr , 1 ) ! = 0 ) {
2018-03-17 09:44:34 +01:00
goto cleanup_glyph ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
glyph_bitmap = ( FT_BitmapGlyph ) glyph ;
ret = _bitmap_to_character ( glyph_bitmap - > bitmap , glyph_bitmap - > top , glyph_bitmap - > left , glyph - > advance . x / 65536.0 ) ;
cleanup_glyph :
FT_Done_Glyph ( glyph ) ;
cleanup_stroker :
FT_Stroker_Done ( stroker ) ;
return ret ;
}
void DynamicFontAtSize : : _update_char ( CharType p_char ) {
2021-05-05 12:44:11 +02:00
if ( char_map . has ( p_char ) ) {
2018-03-17 09:44:34 +01:00
return ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
_THREAD_SAFE_METHOD_
Character character = Character : : not_found ( ) ;
FT_GlyphSlot slot = face - > glyph ;
if ( FT_Get_Char_Index ( face , p_char ) = = 0 ) {
char_map [ p_char ] = character ;
return ;
}
int ft_hinting ;
switch ( font - > hinting ) {
case DynamicFontData : : HINTING_NONE :
ft_hinting = FT_LOAD_NO_HINTING ;
break ;
case DynamicFontData : : HINTING_LIGHT :
ft_hinting = FT_LOAD_TARGET_LIGHT ;
break ;
default :
ft_hinting = FT_LOAD_TARGET_NORMAL ;
break ;
}
2018-06-29 21:38:53 +02:00
int error = FT_Load_Char ( face , p_char , FT_HAS_COLOR ( face ) ? FT_LOAD_COLOR : FT_LOAD_DEFAULT | ( font - > force_autohinter ? FT_LOAD_FORCE_AUTOHINT : 0 ) | ft_hinting ) ;
2018-03-17 09:44:34 +01:00
if ( error ) {
char_map [ p_char ] = character ;
return ;
}
if ( id . outline_size > 0 ) {
character = _make_outline_char ( p_char ) ;
} else {
2018-11-10 22:07:32 +01:00
error = FT_Render_Glyph ( face - > glyph , font - > antialiased ? FT_RENDER_MODE_NORMAL : FT_RENDER_MODE_MONO ) ;
2021-05-05 12:44:11 +02:00
if ( ! error ) {
2018-03-17 09:44:34 +01:00
character = _bitmap_to_character ( slot - > bitmap , slot - > bitmap_top , slot - > bitmap_left , slot - > advance . x / 64.0 ) ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
}
2016-05-02 04:12:30 +02:00
2018-03-17 09:44:34 +01:00
char_map [ p_char ] = character ;
2016-05-02 04:12:30 +02:00
}
2018-03-19 14:48:43 +01:00
void DynamicFontAtSize : : update_oversampling ( ) {
2021-05-05 12:44:11 +02:00
if ( oversampling = = font_oversampling | | ! valid ) {
2018-03-19 14:48:43 +01:00
return ;
2021-05-05 12:44:11 +02:00
}
2017-12-20 01:58:06 +01:00
FT_Done_FreeType ( library ) ;
textures . clear ( ) ;
char_map . clear ( ) ;
oversampling = font_oversampling ;
2018-01-08 18:09:41 +01:00
valid = false ;
2017-12-20 01:58:06 +01:00
_load ( ) ;
}
2016-05-02 04:12:30 +02:00
DynamicFontAtSize : : DynamicFontAtSize ( ) {
2017-03-05 16:44:50 +01:00
valid = false ;
rect_margin = 1 ;
ascent = 1 ;
descent = 1 ;
linegap = 1 ;
texture_flags = 0 ;
2017-12-19 22:48:30 +01:00
oversampling = font_oversampling ;
2018-01-16 14:22:59 +01:00
scale_color_font = 1 ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
DynamicFontAtSize : : ~ DynamicFontAtSize ( ) {
2016-05-29 16:37:26 +02:00
if ( valid ) {
2017-03-05 16:44:50 +01:00
FT_Done_FreeType ( library ) ;
2016-05-29 16:37:26 +02:00
}
2018-01-07 16:56:46 +01:00
font - > size_cache . erase ( id ) ;
2018-01-21 20:18:52 +01:00
font . unref ( ) ;
2016-05-02 04:12:30 +02:00
}
/////////////////////////
2020-07-25 21:42:01 +02:00
void DynamicFont : : _reload_cache ( const char * p_triggering_property ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_COND ( cache_id . size < 1 ) ;
2018-03-17 09:44:34 +01:00
if ( ! data . is_valid ( ) ) {
data_at_size . unref ( ) ;
outline_data_at_size . unref ( ) ;
2019-12-10 14:26:42 +01:00
fallbacks . resize ( 0 ) ;
2018-03-17 09:44:34 +01:00
fallback_data_at_size . resize ( 0 ) ;
fallback_outline_data_at_size . resize ( 0 ) ;
2016-08-02 16:00:32 +02:00
return ;
2018-03-17 09:44:34 +01:00
}
2017-03-05 16:44:50 +01:00
data_at_size = data - > _get_dynamic_font_at_size ( cache_id ) ;
2018-03-17 09:44:34 +01:00
if ( outline_cache_id . outline_size > 0 ) {
outline_data_at_size = data - > _get_dynamic_font_at_size ( outline_cache_id ) ;
fallback_outline_data_at_size . resize ( fallback_data_at_size . size ( ) ) ;
} else {
outline_data_at_size . unref ( ) ;
fallback_outline_data_at_size . resize ( 0 ) ;
}
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < fallbacks . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
fallback_data_at_size . write [ i ] = fallbacks . write [ i ] - > _get_dynamic_font_at_size ( cache_id ) ;
2021-05-05 12:44:11 +02:00
if ( outline_cache_id . outline_size > 0 ) {
2018-07-25 03:11:03 +02:00
fallback_outline_data_at_size . write [ i ] = fallbacks . write [ i ] - > _get_dynamic_font_at_size ( outline_cache_id ) ;
2021-05-05 12:44:11 +02:00
}
2016-08-02 16:00:32 +02:00
}
emit_changed ( ) ;
2020-07-25 21:42:01 +02:00
_change_notify ( p_triggering_property ) ;
2016-08-02 16:00:32 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_font_data ( const Ref < DynamicFontData > & p_data ) {
data = p_data ;
2020-07-25 21:42:01 +02:00
_reload_cache ( ) ; // not passing the prop name as clearing the font data also clears fallbacks
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
Ref < DynamicFontData > DynamicFont : : get_font_data ( ) const {
2016-05-02 04:12:30 +02:00
return data ;
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_size ( int p_size ) {
2021-05-05 12:44:11 +02:00
if ( cache_id . size = = p_size ) {
2016-05-02 04:12:30 +02:00
return ;
2021-05-05 12:44:11 +02:00
}
2017-03-05 16:44:50 +01:00
cache_id . size = p_size ;
2018-03-17 09:44:34 +01:00
outline_cache_id . size = p_size ;
2020-07-25 21:42:01 +02:00
_reload_cache ( " size " ) ;
2016-05-02 04:12:30 +02:00
}
2016-08-02 09:01:51 +02:00
2017-03-05 16:44:50 +01:00
int DynamicFont : : get_size ( ) const {
2016-08-02 16:00:32 +02:00
return cache_id . size ;
2016-08-02 09:01:51 +02:00
}
2018-03-17 09:44:34 +01:00
void DynamicFont : : set_outline_size ( int p_size ) {
2021-05-05 12:44:11 +02:00
if ( outline_cache_id . outline_size = = p_size ) {
2018-03-17 09:44:34 +01:00
return ;
2021-05-05 12:44:11 +02:00
}
2018-03-17 09:44:34 +01:00
ERR_FAIL_COND ( p_size < 0 | | p_size > UINT8_MAX ) ;
outline_cache_id . outline_size = p_size ;
2020-07-25 21:42:01 +02:00
_reload_cache ( " outline_size " ) ;
2018-03-17 09:44:34 +01:00
}
int DynamicFont : : get_outline_size ( ) const {
return outline_cache_id . outline_size ;
}
void DynamicFont : : set_outline_color ( Color p_color ) {
if ( p_color ! = outline_color ) {
outline_color = p_color ;
emit_changed ( ) ;
2020-07-25 21:42:01 +02:00
_change_notify ( " outline_color " ) ;
2018-03-17 09:44:34 +01:00
}
}
Color DynamicFont : : get_outline_color ( ) const {
return outline_color ;
}
2017-03-05 16:44:50 +01:00
bool DynamicFont : : get_use_mipmaps ( ) const {
2016-08-02 16:00:32 +02:00
return cache_id . mipmaps ;
2016-08-02 09:01:51 +02:00
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_use_mipmaps ( bool p_enable ) {
2021-05-05 12:44:11 +02:00
if ( cache_id . mipmaps = = p_enable ) {
2016-08-02 09:01:51 +02:00
return ;
2021-05-05 12:44:11 +02:00
}
2017-03-05 16:44:50 +01:00
cache_id . mipmaps = p_enable ;
2018-03-17 09:44:34 +01:00
outline_cache_id . mipmaps = p_enable ;
2016-08-02 16:00:32 +02:00
_reload_cache ( ) ;
2016-08-02 09:01:51 +02:00
}
2017-03-05 16:44:50 +01:00
bool DynamicFont : : get_use_filter ( ) const {
2016-08-02 16:00:32 +02:00
return cache_id . filter ;
2016-08-02 09:01:51 +02:00
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_use_filter ( bool p_enable ) {
2021-05-05 12:44:11 +02:00
if ( cache_id . filter = = p_enable ) {
2016-08-02 09:01:51 +02:00
return ;
2021-05-05 12:44:11 +02:00
}
2017-03-05 16:44:50 +01:00
cache_id . filter = p_enable ;
2018-03-17 09:44:34 +01:00
outline_cache_id . filter = p_enable ;
2016-08-02 16:00:32 +02:00
_reload_cache ( ) ;
2016-08-02 09:01:51 +02:00
}
2018-11-10 22:07:32 +01:00
bool DynamicFontData : : is_antialiased ( ) const {
return antialiased ;
}
void DynamicFontData : : set_antialiased ( bool p_antialiased ) {
2021-05-05 12:44:11 +02:00
if ( antialiased = = p_antialiased ) {
2018-11-10 22:07:32 +01:00
return ;
2021-05-05 12:44:11 +02:00
}
2018-11-10 22:07:32 +01:00
antialiased = p_antialiased ;
}
2018-02-27 15:04:08 +01:00
DynamicFontData : : Hinting DynamicFontData : : get_hinting ( ) const {
return hinting ;
}
void DynamicFontData : : set_hinting ( Hinting p_hinting ) {
2021-05-05 12:44:11 +02:00
if ( hinting = = p_hinting ) {
2018-02-27 15:04:08 +01:00
return ;
2021-05-05 12:44:11 +02:00
}
2018-02-27 15:04:08 +01:00
hinting = p_hinting ;
}
2017-03-05 16:44:50 +01:00
int DynamicFont : : get_spacing ( int p_type ) const {
if ( p_type = = SPACING_TOP ) {
2016-08-02 12:05:20 +02:00
return spacing_top ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_BOTTOM ) {
2016-08-02 12:05:20 +02:00
return spacing_bottom ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_CHAR ) {
2016-08-02 12:05:20 +02:00
return spacing_char ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_SPACE ) {
2016-08-02 12:05:20 +02:00
return spacing_space ;
}
return 0 ;
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_spacing ( int p_type , int p_value ) {
if ( p_type = = SPACING_TOP ) {
spacing_top = p_value ;
2020-07-25 21:42:01 +02:00
_change_notify ( " extra_spacing_top " ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_BOTTOM ) {
spacing_bottom = p_value ;
2020-07-25 21:42:01 +02:00
_change_notify ( " extra_spacing_bottom " ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_CHAR ) {
spacing_char = p_value ;
2020-07-25 21:42:01 +02:00
_change_notify ( " extra_spacing_char " ) ;
2017-03-05 16:44:50 +01:00
} else if ( p_type = = SPACING_SPACE ) {
spacing_space = p_value ;
2020-07-25 21:42:01 +02:00
_change_notify ( " extra_spacing_space " ) ;
2016-08-02 12:05:20 +02:00
}
emit_changed ( ) ;
}
2017-03-05 16:44:50 +01:00
float DynamicFont : : get_height ( ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2016-05-02 04:12:30 +02:00
return 1 ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
return data_at_size - > get_height ( ) + spacing_top + spacing_bottom ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
float DynamicFont : : get_ascent ( ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2016-05-02 04:12:30 +02:00
return 1 ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
return data_at_size - > get_ascent ( ) + spacing_top ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
float DynamicFont : : get_descent ( ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2016-05-02 04:12:30 +02:00
return 1 ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
return data_at_size - > get_descent ( ) + spacing_bottom ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
Size2 DynamicFont : : get_char_size ( CharType p_char , CharType p_next ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
return Size2 ( 1 , 1 ) ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2017-03-05 16:44:50 +01:00
Size2 ret = data_at_size - > get_char_size ( p_char , p_next , fallback_data_at_size ) ;
2021-05-05 12:44:11 +02:00
if ( p_char = = ' ' ) {
2017-03-05 16:44:50 +01:00
ret . width + = spacing_space + spacing_char ;
2021-05-05 12:44:11 +02:00
} else if ( p_next ) {
2017-03-05 16:44:50 +01:00
ret . width + = spacing_char ;
2021-05-05 12:44:11 +02:00
}
2016-08-02 12:05:20 +02:00
return ret ;
2016-05-02 04:12:30 +02:00
}
2020-07-18 14:53:37 +02:00
String DynamicFont : : get_available_chars ( ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2020-07-18 14:53:37 +02:00
return " " ;
2021-05-05 12:44:11 +02:00
}
2020-07-18 14:53:37 +02:00
String chars = data_at_size - > get_available_chars ( ) ;
for ( int i = 0 ; i < fallback_data_at_size . size ( ) ; i + + ) {
String fallback_chars = fallback_data_at_size [ i ] - > get_available_chars ( ) ;
for ( int j = 0 ; j < fallback_chars . length ( ) ; j + + ) {
if ( chars . find_char ( fallback_chars [ j ] ) = = - 1 ) {
chars + = fallback_chars [ j ] ;
}
}
}
return chars ;
}
2017-03-05 16:44:50 +01:00
bool DynamicFont : : is_distance_field_hint ( ) const {
2016-05-02 04:12:30 +02:00
return false ;
}
2018-03-17 09:44:34 +01:00
bool DynamicFont : : has_outline ( ) const {
return outline_cache_id . outline_size > 0 ;
}
float DynamicFont : : draw_char ( RID p_canvas_item , const Point2 & p_pos , CharType p_char , CharType p_next , const Color & p_modulate , bool p_outline ) const {
2021-05-05 12:44:11 +02:00
if ( ! data_at_size . is_valid ( ) ) {
2016-05-02 04:12:30 +02:00
return 0 ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2021-02-15 17:03:18 +01:00
int spacing = spacing_char ;
if ( p_char = = ' ' ) {
spacing + = spacing_space ;
}
2020-12-25 12:24:08 +01:00
if ( p_outline ) {
if ( outline_data_at_size . is_valid ( ) & & outline_cache_id . outline_size > 0 ) {
2021-05-20 12:47:34 +02:00
outline_data_at_size - > draw_char ( p_canvas_item , p_pos , p_char , p_next , p_modulate * outline_color , fallback_outline_data_at_size , false , true ) ; // Draw glyph outline.
2020-12-25 12:24:08 +01:00
}
2021-02-15 17:03:18 +01:00
return data_at_size - > draw_char ( p_canvas_item , p_pos , p_char , p_next , p_modulate , fallback_data_at_size , true , false ) + spacing ; // Return advance of the base glyph.
2020-12-25 12:24:08 +01:00
} else {
2021-02-15 17:03:18 +01:00
return data_at_size - > draw_char ( p_canvas_item , p_pos , p_char , p_next , p_modulate , fallback_data_at_size , false , false ) + spacing ; // Draw base glyph and return advance.
2020-12-25 12:24:08 +01:00
}
2016-05-31 00:41:32 +02:00
}
2018-03-17 09:44:34 +01:00
2017-03-05 16:44:50 +01:00
void DynamicFont : : set_fallback ( int p_idx , const Ref < DynamicFontData > & p_data ) {
2016-05-31 00:41:32 +02:00
ERR_FAIL_COND ( p_data . is_null ( ) ) ;
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_idx , fallbacks . size ( ) ) ;
2018-07-25 03:11:03 +02:00
fallbacks . write [ p_idx ] = p_data ;
fallback_data_at_size . write [ p_idx ] = fallbacks . write [ p_idx ] - > _get_dynamic_font_at_size ( cache_id ) ;
2016-05-31 00:41:32 +02:00
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : add_fallback ( const Ref < DynamicFontData > & p_data ) {
2016-05-31 00:41:32 +02:00
ERR_FAIL_COND ( p_data . is_null ( ) ) ;
fallbacks . push_back ( p_data ) ;
2018-07-25 03:11:03 +02:00
fallback_data_at_size . push_back ( fallbacks . write [ fallbacks . size ( ) - 1 ] - > _get_dynamic_font_at_size ( cache_id ) ) ; //const..
2021-05-05 12:44:11 +02:00
if ( outline_cache_id . outline_size > 0 ) {
2018-07-25 03:11:03 +02:00
fallback_outline_data_at_size . push_back ( fallbacks . write [ fallbacks . size ( ) - 1 ] - > _get_dynamic_font_at_size ( outline_cache_id ) ) ;
2021-05-05 12:44:11 +02:00
}
2016-05-31 00:41:32 +02:00
2016-06-01 03:28:27 +02:00
emit_changed ( ) ;
_change_notify ( ) ;
2016-05-31 00:41:32 +02:00
}
int DynamicFont : : get_fallback_count ( ) const {
return fallbacks . size ( ) ;
}
Ref < DynamicFontData > DynamicFont : : get_fallback ( int p_idx ) const {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX_V ( p_idx , fallbacks . size ( ) , Ref < DynamicFontData > ( ) ) ;
2016-05-31 00:41:32 +02:00
return fallbacks [ p_idx ] ;
}
void DynamicFont : : remove_fallback ( int p_idx ) {
2017-03-05 16:44:50 +01:00
ERR_FAIL_INDEX ( p_idx , fallbacks . size ( ) ) ;
2016-05-31 00:41:32 +02:00
fallbacks . remove ( p_idx ) ;
fallback_data_at_size . remove ( p_idx ) ;
2016-06-01 03:28:27 +02:00
emit_changed ( ) ;
2016-05-31 00:41:32 +02:00
_change_notify ( ) ;
}
2017-03-05 16:44:50 +01:00
bool DynamicFont : : _set ( const StringName & p_name , const Variant & p_value ) {
2016-05-31 00:41:32 +02:00
String str = p_name ;
if ( str . begins_with ( " fallback/ " ) ) {
2017-03-05 16:44:50 +01:00
int idx = str . get_slicec ( ' / ' , 1 ) . to_int ( ) ;
2016-05-31 00:41:32 +02:00
Ref < DynamicFontData > fd = p_value ;
if ( fd . is_valid ( ) ) {
2017-03-05 16:44:50 +01:00
if ( idx = = fallbacks . size ( ) ) {
2016-05-31 00:41:32 +02:00
add_fallback ( fd ) ;
return true ;
2017-03-05 16:44:50 +01:00
} else if ( idx > = 0 & & idx < fallbacks . size ( ) ) {
set_fallback ( idx , fd ) ;
2016-05-31 00:41:32 +02:00
return true ;
} else {
return false ;
}
2017-03-05 16:44:50 +01:00
} else if ( idx > = 0 & & idx < fallbacks . size ( ) ) {
2016-05-31 00:41:32 +02:00
remove_fallback ( idx ) ;
return true ;
}
}
return false ;
}
2017-03-05 16:44:50 +01:00
bool DynamicFont : : _get ( const StringName & p_name , Variant & r_ret ) const {
2016-05-31 00:41:32 +02:00
String str = p_name ;
if ( str . begins_with ( " fallback/ " ) ) {
2017-03-05 16:44:50 +01:00
int idx = str . get_slicec ( ' / ' , 1 ) . to_int ( ) ;
2016-05-31 00:41:32 +02:00
2017-03-05 16:44:50 +01:00
if ( idx = = fallbacks . size ( ) ) {
r_ret = Ref < DynamicFontData > ( ) ;
2016-05-31 00:41:32 +02:00
return true ;
2017-03-05 16:44:50 +01:00
} else if ( idx > = 0 & & idx < fallbacks . size ( ) ) {
r_ret = get_fallback ( idx ) ;
2016-05-31 00:41:32 +02:00
return true ;
}
}
return false ;
}
2017-03-05 16:44:50 +01:00
void DynamicFont : : _get_property_list ( List < PropertyInfo > * p_list ) const {
for ( int i = 0 ; i < fallbacks . size ( ) ; i + + ) {
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " fallback/ " + itos ( i ) , PROPERTY_HINT_RESOURCE_TYPE , " DynamicFontData " ) ) ;
2016-05-31 00:41:32 +02:00
}
2017-03-05 16:44:50 +01:00
p_list - > push_back ( PropertyInfo ( Variant : : OBJECT , " fallback/ " + itos ( fallbacks . size ( ) ) , PROPERTY_HINT_RESOURCE_TYPE , " DynamicFontData " ) ) ;
2016-05-31 00:41:32 +02:00
}
void DynamicFont : : _bind_methods ( ) {
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " set_font_data " , " data " ) , & DynamicFont : : set_font_data ) ;
ClassDB : : bind_method ( D_METHOD ( " get_font_data " ) , & DynamicFont : : get_font_data ) ;
2017-03-05 16:44:50 +01:00
2020-07-18 14:53:37 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_available_chars " ) , & DynamicFont : : get_available_chars ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_size " , " data " ) , & DynamicFont : : set_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_size " ) , & DynamicFont : : get_size ) ;
2018-03-17 09:44:34 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_outline_size " , " size " ) , & DynamicFont : : set_outline_size ) ;
ClassDB : : bind_method ( D_METHOD ( " get_outline_size " ) , & DynamicFont : : get_outline_size ) ;
ClassDB : : bind_method ( D_METHOD ( " set_outline_color " , " color " ) , & DynamicFont : : set_outline_color ) ;
ClassDB : : bind_method ( D_METHOD ( " get_outline_color " ) , & DynamicFont : : get_outline_color ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_use_mipmaps " , " enable " ) , & DynamicFont : : set_use_mipmaps ) ;
ClassDB : : bind_method ( D_METHOD ( " get_use_mipmaps " ) , & DynamicFont : : get_use_mipmaps ) ;
ClassDB : : bind_method ( D_METHOD ( " set_use_filter " , " enable " ) , & DynamicFont : : set_use_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " get_use_filter " ) , & DynamicFont : : get_use_filter ) ;
ClassDB : : bind_method ( D_METHOD ( " set_spacing " , " type " , " value " ) , & DynamicFont : : set_spacing ) ;
ClassDB : : bind_method ( D_METHOD ( " get_spacing " , " type " ) , & DynamicFont : : get_spacing ) ;
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_fallback " , " data " ) , & DynamicFont : : add_fallback ) ;
ClassDB : : bind_method ( D_METHOD ( " set_fallback " , " idx " , " data " ) , & DynamicFont : : set_fallback ) ;
ClassDB : : bind_method ( D_METHOD ( " get_fallback " , " idx " ) , & DynamicFont : : get_fallback ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " remove_fallback " , " idx " ) , & DynamicFont : : remove_fallback ) ;
ClassDB : : bind_method ( D_METHOD ( " get_fallback_count " ) , & DynamicFont : : get_fallback_count ) ;
ADD_GROUP ( " Settings " , " " ) ;
2020-01-21 20:51:10 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " size " , PROPERTY_HINT_RANGE , " 1,1024,1 " ) , " set_size " , " get_size " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " outline_size " , PROPERTY_HINT_RANGE , " 0,1024,1 " ) , " set_outline_size " , " get_outline_size " ) ;
2018-03-17 09:44:34 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : COLOR , " outline_color " ) , " set_outline_color " , " get_outline_color " ) ;
2017-03-05 16:44:50 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_mipmaps " ) , " set_use_mipmaps " , " get_use_mipmaps " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " use_filter " ) , " set_use_filter " , " get_use_filter " ) ;
ADD_GROUP ( " Extra Spacing " , " extra_spacing " ) ;
2018-11-08 15:30:02 +01:00
ADD_PROPERTYI ( PropertyInfo ( Variant : : INT , " extra_spacing_top " ) , " set_spacing " , " get_spacing " , SPACING_TOP ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : INT , " extra_spacing_bottom " ) , " set_spacing " , " get_spacing " , SPACING_BOTTOM ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : INT , " extra_spacing_char " ) , " set_spacing " , " get_spacing " , SPACING_CHAR ) ;
ADD_PROPERTYI ( PropertyInfo ( Variant : : INT , " extra_spacing_space " ) , " set_spacing " , " get_spacing " , SPACING_SPACE ) ;
2017-03-05 16:44:50 +01:00
ADD_GROUP ( " Font " , " " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : OBJECT , " font_data " , PROPERTY_HINT_RESOURCE_TYPE , " DynamicFontData " ) , " set_font_data " , " get_font_data " ) ;
2017-08-20 17:45:01 +02:00
BIND_ENUM_CONSTANT ( SPACING_TOP ) ;
BIND_ENUM_CONSTANT ( SPACING_BOTTOM ) ;
BIND_ENUM_CONSTANT ( SPACING_CHAR ) ;
BIND_ENUM_CONSTANT ( SPACING_SPACE ) ;
2016-05-02 04:12:30 +02:00
}
2021-01-27 10:43:02 +01:00
Mutex DynamicFont : : dynamic_font_mutex ;
2017-12-20 01:58:06 +01:00
2021-05-04 16:00:45 +02:00
SelfList < DynamicFont > : : List * DynamicFont : : dynamic_fonts = nullptr ;
2017-12-20 01:58:06 +01:00
DynamicFont : : DynamicFont ( ) :
font_list ( this ) {
2018-08-24 16:20:50 +02:00
cache_id . size = 16 ;
2018-08-24 16:22:17 +02:00
outline_cache_id . size = 16 ;
2017-03-05 16:44:50 +01:00
spacing_top = 0 ;
spacing_bottom = 0 ;
spacing_char = 0 ;
spacing_space = 0 ;
2018-03-17 09:44:34 +01:00
outline_color = Color ( 1 , 1 , 1 ) ;
2021-01-27 10:43:02 +01:00
dynamic_font_mutex . lock ( ) ;
dynamic_fonts - > add ( & font_list ) ;
dynamic_font_mutex . unlock ( ) ;
2016-05-02 04:12:30 +02:00
}
DynamicFont : : ~ DynamicFont ( ) {
2021-01-27 10:43:02 +01:00
dynamic_font_mutex . lock ( ) ;
dynamic_fonts - > remove ( & font_list ) ;
dynamic_font_mutex . unlock ( ) ;
2017-12-20 01:58:06 +01:00
}
void DynamicFont : : initialize_dynamic_fonts ( ) {
2018-10-20 11:13:06 +02:00
dynamic_fonts = memnew ( SelfList < DynamicFont > : : List ( ) ) ;
2017-12-20 01:58:06 +01:00
}
void DynamicFont : : finish_dynamic_fonts ( ) {
2018-10-20 11:13:06 +02:00
memdelete ( dynamic_fonts ) ;
2021-05-04 16:00:45 +02:00
dynamic_fonts = nullptr ;
2017-12-20 01:58:06 +01:00
}
void DynamicFont : : update_oversampling ( ) {
2021-05-04 14:20:36 +02:00
Vector < Ref < DynamicFont > > changed ;
2018-01-08 18:09:41 +01:00
2021-01-27 10:43:02 +01:00
dynamic_font_mutex . lock ( ) ;
2018-01-08 18:09:41 +01:00
2018-10-20 11:13:06 +02:00
SelfList < DynamicFont > * E = dynamic_fonts - > first ( ) ;
2017-12-20 01:58:06 +01:00
while ( E ) {
2018-03-19 14:48:43 +01:00
if ( E - > self ( ) - > data_at_size . is_valid ( ) ) {
E - > self ( ) - > data_at_size - > update_oversampling ( ) ;
2018-05-11 08:59:56 +02:00
if ( E - > self ( ) - > outline_data_at_size . is_valid ( ) ) {
E - > self ( ) - > outline_data_at_size - > update_oversampling ( ) ;
}
2018-10-02 21:55:06 +02:00
for ( int i = 0 ; i < E - > self ( ) - > fallback_data_at_size . size ( ) ; i + + ) {
if ( E - > self ( ) - > fallback_data_at_size [ i ] . is_valid ( ) ) {
E - > self ( ) - > fallback_data_at_size . write [ i ] - > update_oversampling ( ) ;
if ( E - > self ( ) - > has_outline ( ) & & E - > self ( ) - > fallback_outline_data_at_size [ i ] . is_valid ( ) ) {
E - > self ( ) - > fallback_outline_data_at_size . write [ i ] - > update_oversampling ( ) ;
}
}
}
2018-01-21 20:18:52 +01:00
changed . push_back ( Ref < DynamicFont > ( E - > self ( ) ) ) ;
2017-12-20 01:58:06 +01:00
}
2018-10-02 21:55:06 +02:00
2017-12-20 01:58:06 +01:00
E = E - > next ( ) ;
}
2018-01-08 18:09:41 +01:00
2021-01-27 10:43:02 +01:00
dynamic_font_mutex . unlock ( ) ;
2018-01-08 18:09:41 +01:00
for ( int i = 0 ; i < changed . size ( ) ; i + + ) {
2018-07-25 03:11:03 +02:00
changed . write [ i ] - > emit_changed ( ) ;
2018-01-08 18:09:41 +01:00
}
2016-05-02 04:12:30 +02:00
}
/////////////////////////
2017-03-05 16:44:50 +01:00
RES ResourceFormatLoaderDynamicFont : : load ( const String & p_path , const String & p_original_path , Error * r_error ) {
2021-05-05 12:44:11 +02:00
if ( r_error ) {
2017-03-05 16:44:50 +01:00
* r_error = ERR_FILE_CANT_OPEN ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2016-05-29 16:37:26 +02:00
Ref < DynamicFontData > dfont ;
2017-01-14 18:03:38 +01:00
dfont . instance ( ) ;
2016-05-29 16:37:26 +02:00
dfont - > set_font_path ( p_path ) ;
2016-05-02 04:12:30 +02:00
2021-05-05 12:44:11 +02:00
if ( r_error ) {
2017-03-05 16:44:50 +01:00
* r_error = OK ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
2016-05-29 16:37:26 +02:00
return dfont ;
2016-05-02 04:12:30 +02:00
}
void ResourceFormatLoaderDynamicFont : : get_recognized_extensions ( List < String > * p_extensions ) const {
p_extensions - > push_back ( " ttf " ) ;
2016-05-29 16:37:26 +02:00
p_extensions - > push_back ( " otf " ) ;
2021-08-24 04:29:18 +02:00
// Only WOFF1 is supported as WOFF2 requires a Brotli decompression library to be linked.
p_extensions - > push_back ( " woff " ) ;
2016-05-02 04:12:30 +02:00
}
2017-03-05 16:44:50 +01:00
bool ResourceFormatLoaderDynamicFont : : handles_type ( const String & p_type ) const {
return ( p_type = = " DynamicFontData " ) ;
2016-05-02 04:12:30 +02:00
}
String ResourceFormatLoaderDynamicFont : : get_resource_type ( const String & p_path ) const {
2017-01-14 04:51:09 +01:00
String el = p_path . get_extension ( ) . to_lower ( ) ;
2021-08-24 04:29:18 +02:00
if ( el = = " ttf " | | el = = " otf " | | el = = " woff " ) {
2016-05-02 04:12:30 +02:00
return " DynamicFontData " ;
2021-05-05 12:44:11 +02:00
}
2016-05-02 04:12:30 +02:00
return " " ;
}
2016-05-29 16:37:26 +02:00
# endif