diff options
author | Juan Linietsky <reduzio@gmail.com> | 2016-05-29 11:37:26 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2016-05-29 11:37:52 -0300 |
commit | 3e8eb396d7cfec8a96efb78719c0556f1beccf68 (patch) | |
tree | 3f6e9adbac4c20989812b338ca7bc3a0b65f568d /scene | |
parent | a5777994cbc06183af7db7d8233434f245d5b089 (diff) |
Finalized DynamicFont implementation
-DynamicFont uses Freetype by default
-Editor fonts are now scalable thanks to this
-Cleaned up documentation browser and added fonts for this
Diffstat (limited to 'scene')
-rw-r--r-- | scene/gui/text_edit.cpp | 2 | ||||
-rw-r--r-- | scene/register_scene_types.cpp | 5 | ||||
-rw-r--r-- | scene/resources/dynamic_font.cpp | 301 | ||||
-rw-r--r-- | scene/resources/dynamic_font.h | 76 | ||||
-rw-r--r-- | scene/resources/dynamic_font_stb.cpp | 527 | ||||
-rw-r--r-- | scene/resources/dynamic_font_stb.h | 178 |
6 files changed, 929 insertions, 160 deletions
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 03024daff5..d021c7b40e 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -412,7 +412,7 @@ void TextEdit::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { _update_caches(); - }; + } break; case NOTIFICATION_DRAW: { int line_number_char_count=0; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 8327473a60..1fd1c77dca 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -133,6 +133,7 @@ #include "scene/resources/mesh_data_tool.h" #include "scene/resources/scene_preloader.h" #include "scene/resources/dynamic_font.h" +#include "scene/resources/dynamic_font_stb.h" #include "scene/main/timer.h" @@ -254,7 +255,6 @@ void register_scene_types() { resource_loader_wav = memnew( ResourceFormatLoaderWAV ); ResourceLoader::add_resource_format_loader( resource_loader_wav ); - resource_loader_dynamic_font = memnew( ResourceFormatLoaderDynamicFont ); ResourceLoader::add_resource_format_loader( resource_loader_dynamic_font ); @@ -578,8 +578,10 @@ void register_scene_types() { ObjectTypeDB::register_type<Animation>(); ObjectTypeDB::register_virtual_type<Font>(); ObjectTypeDB::register_type<BitmapFont>(); + ObjectTypeDB::register_type<DynamicFontData>(); ObjectTypeDB::register_type<DynamicFont>(); + ObjectTypeDB::register_type<StyleBoxEmpty>(); ObjectTypeDB::register_type<StyleBoxTexture>(); ObjectTypeDB::register_type<StyleBoxFlat>(); @@ -647,6 +649,7 @@ void unregister_scene_types() { memdelete( resource_loader_image ); memdelete( resource_loader_wav ); memdelete( resource_loader_dynamic_font ); + #ifdef TOOLS_ENABLED diff --git a/scene/resources/dynamic_font.cpp b/scene/resources/dynamic_font.cpp index 19aa0e79cc..c76b5f3d3a 100644 --- a/scene/resources/dynamic_font.cpp +++ b/scene/resources/dynamic_font.cpp @@ -1,65 +1,11 @@ +#ifdef FREETYPE_ENABLED #include "dynamic_font.h" -#define STB_TRUETYPE_IMPLEMENTATION -#include "stb_truetype.h" #include "os/file_access.h" -void DynamicFontData::lock() { - fr=font_data.read(); - - if (fr.ptr()!=last_data_ptr) { - - last_data_ptr=fr.ptr(); - - if (!stbtt_InitFont(&info, last_data_ptr, 0)) { - valid=false; - } else { - valid=true; - } - - last_data_ptr=fr.ptr(); - } -} - -void DynamicFontData::unlock() { - - fr = DVector<uint8_t>::Read(); -} - -void DynamicFontData::set_font_data(const DVector<uint8_t>& p_font) { - //clear caches and stuff - ERR_FAIL_COND(font_data.size()) ; - font_data=p_font; - - lock(); - - if (valid) { - stbtt_GetFontVMetrics(&info, &ascent, &descent, &linegap); - descent=-descent + linegap; - - for(int i=32;i<1024;i++) { - for(int j=32;j<1024;j++) { - - int kern = stbtt_GetCodepointKernAdvance(&info, i,j); - if (kern!=0) { - KerningPairKey kpk; - kpk.A=i; - kpk.B=j; - kerning_map[kpk]=kern; - } - } - } - } - - unlock(); - //clear existing stuff - - ERR_FAIL_COND(!valid); -} Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) { - ERR_FAIL_COND_V(!valid,Ref<DynamicFontAtSize>()); if (size_cache.has(p_size)) { return Ref<DynamicFontAtSize>( size_cache[p_size] ); @@ -67,6 +13,7 @@ Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) { Ref<DynamicFontAtSize> dfas; + dfas.instance(); dfas->font=Ref<DynamicFontData>( this ); @@ -74,21 +21,34 @@ Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) { size_cache[p_size]=dfas.ptr(); dfas->size=p_size; + dfas->_load(); + + return dfas; - lock(); +} - dfas->scale = stbtt_ScaleForPixelHeight(&info, p_size); +void DynamicFontData::set_font_ptr(const uint8_t* p_font_mem,int p_font_mem_size) { - unlock(); + font_mem=p_font_mem; + font_mem_size=p_font_mem_size; +} - return dfas; +void DynamicFontData::set_font_path(const String& p_path) { + + font_path=p_path; +} + +void DynamicFontData::set_force_autohinter(bool p_force) { + force_autohinter=p_force; } DynamicFontData::DynamicFontData() { - last_data_ptr=NULL; - valid=false; + + force_autohinter=false; + font_mem=NULL; + font_mem_size=0; } DynamicFontData::~DynamicFontData() @@ -100,22 +60,106 @@ DynamicFontData::~DynamicFontData() //////////////////// +Error DynamicFontAtSize::_load() { + + + int error = FT_Init_FreeType( &library ); + + ERR_EXPLAIN(TTR("Error initializing FreeType.")); + ERR_FAIL_COND_V( error !=0, ERR_CANT_CREATE ); + + if (font->font_path!=String()) { + + FileAccess *f=FileAccess::open(font->font_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,ERR_CANT_OPEN); + + memset(&stream,0,sizeof(FT_StreamRec)); + stream.base=NULL; + stream.size=f->get_len(); + stream.pos=0; + stream.descriptor.pointer=f; + stream.read=_ft_stream_io; + stream.close=_ft_stream_close; + + FT_Open_Args fargs; + memset(&fargs,0,sizeof(FT_Open_Args)); + fargs.flags=FT_OPEN_STREAM; + fargs.stream=&stream; + error = FT_Open_Face( library,&fargs,0,&face); + } else if (font->font_mem) { + + memset(&stream,0,sizeof(FT_StreamRec)); + stream.base=(unsigned char*)font->font_mem; + stream.size=font->font_mem_size; + stream.pos=0; + + FT_Open_Args fargs; + 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); + + } else { + ERR_EXPLAIN("DynamicFont uninitialized"); + ERR_FAIL_V(ERR_UNCONFIGURED); + } + + //error = FT_New_Face( library, src_path.utf8().get_data(),0,&face ); + + if ( error == FT_Err_Unknown_File_Format ) { + ERR_EXPLAIN(TTR("Unknown font format.")); + FT_Done_FreeType( library ); + + } else if ( error ) { + + ERR_EXPLAIN(TTR("Error loading font.")); + FT_Done_FreeType( library ); + + } + + ERR_FAIL_COND_V(error,ERR_FILE_CANT_OPEN); + + + /*error = FT_Set_Char_Size(face,0,64*size,512,512); + + if ( error ) { + FT_Done_FreeType( library ); + ERR_EXPLAIN(TTR("Invalid font size.")); + ERR_FAIL_COND_V( error, ERR_INVALID_PARAMETER ); + }*/ + + error = FT_Set_Pixel_Sizes(face,0,size); + + ascent=face->size->metrics.ascender>>6; + descent=-face->size->metrics.descender>>6; + linegap=0; + + //print_line("ASCENT: "+itos(ascent)+" descent "+itos(descent)+" hinted: "+itos(face->face_flags&FT_FACE_FLAG_HINTER)); + + valid=true; + return OK; +} + float DynamicFontAtSize::get_height() const { - return (font->ascent+font->descent)*scale; + return ascent+descent; } float DynamicFontAtSize::get_ascent() const { - return font->ascent*scale; + return ascent; } float DynamicFontAtSize::get_descent() const { - return font->descent*scale; + return descent; } Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const { + if (!valid) + return Size2(1,1); const_cast<DynamicFontAtSize*>(this)->_update_char(p_char); const Character *c = char_map.getptr(p_char); @@ -124,14 +168,9 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const { Size2 ret( c->advance, get_height()); if (p_next) { - DynamicFontData::KerningPairKey kpk; - kpk.A=p_char; - kpk.B=p_next; - - const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk); - if (K) { - ret.x+=K->get()*scale; - } + FT_Vector delta; + FT_Get_Kerning( face, p_char,p_next, FT_KERNING_DEFAULT, &delta ); + ret.x+=delta.x>>6; } @@ -141,6 +180,9 @@ Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const { float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const { + if (!valid) + return 0; + const_cast<DynamicFontAtSize*>(this)->_update_char(p_char); const Character * c = char_map.getptr(p_char); @@ -161,58 +203,84 @@ float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const float ret = c->advance; if (p_next) { - DynamicFontData::KerningPairKey kpk; - kpk.A=p_char; - kpk.B=p_next; - const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk); - if (K) { - ret+=K->get()*scale; - } + FT_Vector delta; + FT_Get_Kerning( face, p_char,p_next, FT_KERNING_DEFAULT, &delta ); + ret+=delta.x>>6; } return ret; } +unsigned long DynamicFontAtSize::_ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count ) { + + + FileAccess *f=(FileAccess*)stream->descriptor.pointer; + + if (f->get_pos()!=offset) { + f->seek(offset); + + } + + if (count==0) + return 0; + + return f->get_buffer(buffer,count); +} +void DynamicFontAtSize::_ft_stream_close(FT_Stream stream) { + + FileAccess *f=(FileAccess*)stream->descriptor.pointer; + f->close(); + memdelete(f); +} + void DynamicFontAtSize::_update_char(CharType p_char) { if (char_map.has(p_char)) return; - font->lock(); - + _THREAD_SAFE_METHOD_ - int w,h,xofs,yofs; - unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs ); + FT_GlyphSlot slot = face->glyph; - if (!cpbitmap) { - //no glyph + int error = FT_Load_Char( face, p_char, FT_LOAD_RENDER|(font->force_autohinter?FT_LOAD_FORCE_AUTOHINT:0) ); + if (!error) { + error = FT_Render_Glyph( face->glyph, ft_render_mode_normal ); + } + if (error) { - int advance; - stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); + int advance=0; + //stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale)); Character ch; ch.texture_idx=-1; - ch.advance=advance*scale; + ch.advance=advance; ch.h_align=0; ch.v_align=0; char_map[p_char]=ch; - font->unlock(); return; } + + + int w = slot->bitmap.width; + int h = slot->bitmap.rows; + int p = slot->bitmap.pitch; + int yofs=slot->bitmap_top; + int xofs=slot->bitmap_left; + int advance=slot->advance.x>>6; + + int mw=w+rect_margin*2; int mh=h+rect_margin*2; if (mw>4096 || mh>4096) { - stbtt_FreeBitmap(cpbitmap,NULL); - font->unlock(); ERR_FAIL_COND(mw>4096); ERR_FAIL_COND(mh>4096); } @@ -304,13 +372,14 @@ void DynamicFontAtSize::_update_char(CharType p_char) { { DVector<uint8_t>::Write wr = tex.imgdata.write(); + for(int i=0;i<h;i++) { for(int j=0;j<w;j++) { int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2; ERR_FAIL_COND(ofs >= tex.imgdata.size()); wr[ofs+0]=255; //grayscale as 1 - wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0 + wr[ofs+1]=slot->bitmap.buffer[i*slot->bitmap.width+j]; } } } @@ -322,7 +391,7 @@ void DynamicFontAtSize::_update_char(CharType p_char) { if (tex.texture.is_null()) { tex.texture.instance(); - tex.texture->create_from_image(img,Texture::FLAG_FILTER); + tex.texture->create_from_image(img,0/*Texture::FLAG_FILTER*/); } else { tex.texture->set_data(img); //update } @@ -337,13 +406,11 @@ void DynamicFontAtSize::_update_char(CharType p_char) { tex.offsets[k]=tex_y+mh; } - int advance; - stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); Character chr; chr.h_align=xofs; - chr.v_align=yofs + get_ascent(); - chr.advance=advance*scale; + chr.v_align=ascent-yofs;// + ascent - descent; + chr.advance=advance; chr.texture_idx=tex_index; @@ -353,21 +420,24 @@ void DynamicFontAtSize::_update_char(CharType p_char) { char_map[p_char]=chr; - stbtt_FreeBitmap(cpbitmap,NULL); - - font->unlock(); } DynamicFontAtSize::DynamicFontAtSize() { + valid=false; rect_margin=1; + ascent=1; + descent=1; + linegap=1; } DynamicFontAtSize::~DynamicFontAtSize(){ - ERR_FAIL_COND(!font.ptr()); - font->size_cache.erase(size); + if (valid) { + FT_Done_FreeType( library ); + font->size_cache.erase(size); + } } ///////////////////////// @@ -478,34 +548,21 @@ RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String& p_ if (r_error) *r_error=ERR_FILE_CANT_OPEN; + Ref<DynamicFontData> dfont; + dfont.instance();; + dfont->set_font_path(p_path); - FileAccess *f = FileAccess::open(p_path,FileAccess::READ); - ERR_FAIL_COND_V(!f,RES()); - - DVector<uint8_t> data; - - data.resize(f->get_len()); - - ERR_FAIL_COND_V(data.size()==0,RES()); - - { - DVector<uint8_t>::Write w = data.write(); - f->get_buffer(w.ptr(),data.size()); - } - - Ref<DynamicFontData> dfd; - dfd.instance(); - dfd->set_font_data(data); if (r_error) *r_error=OK; - return dfd; + return dfont; } void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const { p_extensions->push_back("ttf"); + p_extensions->push_back("otf"); } bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const { @@ -516,8 +573,10 @@ bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const { String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const { String el = p_path.extension().to_lower(); - if (el=="ttf") + if (el=="ttf" || el=="otf") return "DynamicFontData"; return ""; } + +#endif diff --git a/scene/resources/dynamic_font.h b/scene/resources/dynamic_font.h index ba7249a7b7..f0cf80b042 100644 --- a/scene/resources/dynamic_font.h +++ b/scene/resources/dynamic_font.h @@ -1,10 +1,14 @@ -#ifndef DYNAMICFONT_H -#define DYNAMICFONT_H +#ifndef DYNAMIC_FONT_H +#define DYNAMIC_FONT_H -#include "font.h" -#include "stb_truetype.h" +#ifdef FREETYPE_ENABLED +#include "scene/resources/font.h" +#include "os/thread_safe.h" #include "io/resource_loader.h" +#include <ft2build.h> +#include FT_FREETYPE_H + class DynamicFontAtSize; class DynamicFont; @@ -13,39 +17,16 @@ class DynamicFontData : public Resource { OBJ_TYPE(DynamicFontData,Resource); - bool valid; - - DVector<uint8_t> font_data; - DVector<uint8_t>::Read fr; - const uint8_t* last_data_ptr; - - struct KerningPairKey { - - union { - struct { - uint32_t A,B; - }; - - uint64_t pair; - }; - - _FORCE_INLINE_ bool operator<(const KerningPairKey& p_r) const { return pair<p_r.pair; } - }; - Map<KerningPairKey,int> kerning_map; + const uint8_t *font_mem; + int font_mem_size; + bool force_autohinter; + String font_path; Map<int,DynamicFontAtSize*> size_cache; -friend class DynamicFontAtSize; - - stbtt_fontinfo info; - int ascent; - int descent; - int linegap; - - void lock(); - void unlock(); + friend class DynamicFontAtSize; friend class DynamicFont; @@ -53,7 +34,10 @@ friend class DynamicFont; Ref<DynamicFontAtSize> _get_dynamic_font_at_size(int p_size); public: - void set_font_data(const DVector<uint8_t>& p_font); + void set_font_ptr(const uint8_t* p_font_mem,int p_font_mem_size); + void set_font_path(const String& p_path); + void set_force_autohinter(bool p_force); + DynamicFontData(); ~DynamicFontData(); }; @@ -61,11 +45,21 @@ public: class DynamicFontAtSize : public Reference { - OBJ_TYPE(DynamicFontAtSize,Reference); + OBJ_TYPE(DynamicFontAtSize,Reference) + _THREAD_SAFE_CLASS_ + FT_Library library; /* handle to library */ + FT_Face face; /* handle to face object */ + FT_StreamRec stream; + + int ascent; + int descent; + int linegap; int rect_margin; + bool valid; + struct CharTexture { DVector<uint8_t> imgdata; @@ -88,6 +82,8 @@ class DynamicFontAtSize : public Reference { }; + static unsigned long _ft_stream_io(FT_Stream stream, unsigned long offset, unsigned char* buffer, unsigned long count ); + static void _ft_stream_close(FT_Stream stream); HashMap< CharType, Character > char_map; @@ -95,13 +91,18 @@ class DynamicFontAtSize : public Reference { friend class DynamicFontData; Ref<DynamicFontData> font; - float scale; int size; + + + Error _load(); protected: + + public: + float get_height() const; float get_ascent() const; @@ -126,7 +127,7 @@ class DynamicFont : public Font { Ref<DynamicFontData> data; Ref<DynamicFontAtSize> data_at_size; int size; - + bool valid; protected: @@ -171,5 +172,6 @@ public: }; +#endif -#endif // DYNAMICFONT_H +#endif diff --git a/scene/resources/dynamic_font_stb.cpp b/scene/resources/dynamic_font_stb.cpp new file mode 100644 index 0000000000..344043fcdd --- /dev/null +++ b/scene/resources/dynamic_font_stb.cpp @@ -0,0 +1,527 @@ +#include "dynamic_font_stb.h" + +#ifndef FREETYPE_ENABLED + +#define STB_TRUETYPE_IMPLEMENTATION +#include "stb_truetype.h" +#include "os/file_access.h" + +void DynamicFontData::lock() { + + fr=font_data.read(); + + if (fr.ptr()!=last_data_ptr) { + + last_data_ptr=fr.ptr(); + + if (!stbtt_InitFont(&info, last_data_ptr, 0)) { + valid=false; + } else { + valid=true; + } + + last_data_ptr=fr.ptr(); + } +} + +void DynamicFontData::unlock() { + + fr = DVector<uint8_t>::Read(); +} + +void DynamicFontData::set_font_data(const DVector<uint8_t>& p_font) { + //clear caches and stuff + ERR_FAIL_COND(font_data.size()) ; + font_data=p_font; + + lock(); + + if (valid) { + stbtt_GetFontVMetrics(&info, &ascent, &descent, &linegap); + descent=-descent + linegap; + + for(int i=32;i<1024;i++) { + for(int j=32;j<1024;j++) { + + int kern = stbtt_GetCodepointKernAdvance(&info, i,j); + if (kern!=0) { + KerningPairKey kpk; + kpk.A=i; + kpk.B=j; + kerning_map[kpk]=kern; + } + } + } + } + + unlock(); + //clear existing stuff + + ERR_FAIL_COND(!valid); +} + +Ref<DynamicFontAtSize> DynamicFontData::_get_dynamic_font_at_size(int p_size) { + + ERR_FAIL_COND_V(!valid,Ref<DynamicFontAtSize>()); + + if (size_cache.has(p_size)) { + return Ref<DynamicFontAtSize>( size_cache[p_size] ); + } + + + Ref<DynamicFontAtSize> dfas; + dfas.instance(); + + dfas->font=Ref<DynamicFontData>( this ); + + size_cache[p_size]=dfas.ptr(); + + dfas->size=p_size; + + lock(); + + dfas->scale = stbtt_ScaleForPixelHeight(&info, p_size); + + unlock(); + + return dfas; + +} + +DynamicFontData::DynamicFontData() +{ + last_data_ptr=NULL; + valid=false; +} + +DynamicFontData::~DynamicFontData() +{ + +} + + + +//////////////////// + +float DynamicFontAtSize::get_height() const { + + return (font->ascent+font->descent)*scale; +} + +float DynamicFontAtSize::get_ascent() const { + + return font->ascent*scale; +} +float DynamicFontAtSize::get_descent() const { + + return font->descent*scale; +} + +Size2 DynamicFontAtSize::get_char_size(CharType p_char,CharType p_next) const { + + const_cast<DynamicFontAtSize*>(this)->_update_char(p_char); + + const Character *c = char_map.getptr(p_char); + ERR_FAIL_COND_V(!c,Size2()); + + Size2 ret( c->advance, get_height()); + + if (p_next) { + DynamicFontData::KerningPairKey kpk; + kpk.A=p_char; + kpk.B=p_next; + + const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk); + if (K) { + ret.x+=K->get()*scale; + } + + } + + return ret; +} + + +float DynamicFontAtSize::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const { + + const_cast<DynamicFontAtSize*>(this)->_update_char(p_char); + + const Character * c = char_map.getptr(p_char); + + if (!c) { + return 0; + } + + Point2 cpos=p_pos; + cpos.x+=c->h_align; + cpos.y-=get_ascent(); + cpos.y+=c->v_align; + ERR_FAIL_COND_V( c->texture_idx<-1 || c->texture_idx>=textures.size(),0); + if (c->texture_idx!=-1) + VisualServer::get_singleton()->canvas_item_add_texture_rect_region( p_canvas_item, Rect2( cpos, c->rect.size ), textures[c->texture_idx].texture->get_rid(),c->rect, p_modulate ); + + //textures[c->texture_idx].texture->draw(p_canvas_item,Vector2()); + + float ret = c->advance; + if (p_next) { + DynamicFontData::KerningPairKey kpk; + kpk.A=p_char; + kpk.B=p_next; + + const Map<DynamicFontData::KerningPairKey,int>::Element *K=font->kerning_map.find(kpk); + if (K) { + ret+=K->get()*scale; + } + + } + + return ret; +} + + +void DynamicFontAtSize::_update_char(CharType p_char) { + + if (char_map.has(p_char)) + return; + + font->lock(); + + + int w,h,xofs,yofs; + unsigned char * cpbitmap = stbtt_GetCodepointBitmap(&font->info, scale, scale, p_char, &w, &h, &xofs, &yofs ); + + if (!cpbitmap) { + //no glyph + + int advance; + stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); + //print_line("char has no bitmap: "+itos(p_char)+" but advance is "+itos(advance*scale)); + Character ch; + ch.texture_idx=-1; + ch.advance=advance*scale; + ch.h_align=0; + ch.v_align=0; + + char_map[p_char]=ch; + + font->unlock(); + + return; + } + + int mw=w+rect_margin*2; + int mh=h+rect_margin*2; + + if (mw>4096 || mh>4096) { + + stbtt_FreeBitmap(cpbitmap,NULL); + font->unlock(); + ERR_FAIL_COND(mw>4096); + ERR_FAIL_COND(mh>4096); + } + + //find a texture to fit this... + + int tex_index=-1; + int tex_x=0; + int tex_y=0; + + for(int i=0;i<textures.size();i++) { + + CharTexture &ct=textures[i]; + + if (mw > ct.texture_size || mh > ct.texture_size) //too big for this texture + continue; + + tex_y=0x7FFFFFFF; + tex_x=0; + + for(int j=0;j<ct.texture_size-mw;j++) { + + int max_y=0; + + for(int k=j;k<j+mw;k++) { + + int y = ct.offsets[k]; + if (y>max_y) + max_y=y; + } + + if (max_y<tex_y) { + tex_y=max_y; + tex_x=j; + } + } + + if (tex_y==0x7FFFFFFF || tex_y+mh > ct.texture_size) + continue; //fail, could not fit it here + + tex_index=i; + break; + } + +// print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" X: "+itos(tex_x)+" Y: "+itos(tex_y)); + + if (tex_index==-1) { + //could not find texture to fit, create one + tex_x = 0; + tex_y = 0; + + int texsize = MAX(size*8,256); + if (mw>texsize) + texsize=mw; //special case, adapt to it? + if (mh>texsize) + texsize=mh; //special case, adapt to it? + + texsize=nearest_power_of_2(texsize); + + texsize=MIN(texsize,4096); + + + CharTexture tex; + tex.texture_size=texsize; + tex.imgdata.resize(texsize*texsize*2); //grayscale alpha + + { + //zero texture + DVector<uint8_t>::Write w = tex.imgdata.write(); + ERR_FAIL_COND(texsize*texsize*2 > tex.imgdata.size()); + for(int i=0;i<texsize*texsize*2;i++) { + w[i]=0; + } + } + tex.offsets.resize(texsize); + for(int i=0;i<texsize;i++) //zero offsets + tex.offsets[i]=0; + + textures.push_back(tex); + tex_index=textures.size()-1; + + } + + + //fit character in char texture + + CharTexture &tex=textures[tex_index]; + + { + DVector<uint8_t>::Write wr = tex.imgdata.write(); + + for(int i=0;i<h;i++) { + for(int j=0;j<w;j++) { + + int ofs = ( (i+tex_y+rect_margin)*tex.texture_size+j+tex_x+rect_margin)*2; + ERR_FAIL_COND(ofs >= tex.imgdata.size()); + wr[ofs+0]=255; //grayscale as 1 + wr[ofs+1]=cpbitmap[i*w+j]; //alpha as 0 + } + } + } + + //blit to image and texture + { + + Image img(tex.texture_size,tex.texture_size,0,Image::FORMAT_GRAYSCALE_ALPHA,tex.imgdata); + + if (tex.texture.is_null()) { + tex.texture.instance(); + tex.texture->create_from_image(img,Texture::FLAG_FILTER); + } else { + tex.texture->set_data(img); //update + } + + } + + + // update height array + + for(int k=tex_x;k<tex_x+mw;k++) { + + tex.offsets[k]=tex_y+mh; + } + + int advance; + stbtt_GetCodepointHMetrics(&font->info, p_char, &advance, 0); + + Character chr; + chr.h_align=xofs; + chr.v_align=yofs + get_ascent(); + chr.advance=advance*scale; + chr.texture_idx=tex_index; + + + chr.rect=Rect2(tex_x+rect_margin,tex_y+rect_margin,w,h); + + //print_line("CHAR: "+String::chr(p_char)+" TEX INDEX: "+itos(tex_index)+" RECT: "+chr.rect+" X OFS: "+itos(xofs)+" Y OFS: "+itos(yofs)); + + char_map[p_char]=chr; + + stbtt_FreeBitmap(cpbitmap,NULL); + + font->unlock(); + +} + +DynamicFontAtSize::DynamicFontAtSize() { + + rect_margin=1; +} + +DynamicFontAtSize::~DynamicFontAtSize(){ + + ERR_FAIL_COND(!font.ptr()); + font->size_cache.erase(size); +} + +///////////////////////// + + +void DynamicFont::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_font_data","data:DynamicFontData"),&DynamicFont::set_font_data); + ObjectTypeDB::bind_method(_MD("get_font_data:DynamicFontData"),&DynamicFont::get_font_data); + + ObjectTypeDB::bind_method(_MD("set_size","data"),&DynamicFont::set_size); + ObjectTypeDB::bind_method(_MD("get_size"),&DynamicFont::get_size); + + ADD_PROPERTY(PropertyInfo(Variant::INT,"size"),_SCS("set_size"),_SCS("get_size")); + ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"font",PROPERTY_HINT_RESOURCE_TYPE,"DynamicFontData"),_SCS("set_font_data"),_SCS("get_font_data")); +} + + +void DynamicFont::set_font_data(const Ref<DynamicFontData>& p_data) { + + data=p_data; + data_at_size=data->_get_dynamic_font_at_size(size); +} + +Ref<DynamicFontData> DynamicFont::get_font_data() const{ + + return data; +} + +void DynamicFont::set_size(int p_size){ + + if (size==p_size) + return; + size=p_size; + ERR_FAIL_COND(p_size<1); + if (!data.is_valid()) + return; + data_at_size=data->_get_dynamic_font_at_size(size); + +} +int DynamicFont::get_size() const{ + + return size; +} + +float DynamicFont::get_height() const{ + + if (!data_at_size.is_valid()) + return 1; + + return data_at_size->get_height(); +} + +float DynamicFont::get_ascent() const{ + + if (!data_at_size.is_valid()) + return 1; + + return data_at_size->get_ascent(); +} + +float DynamicFont::get_descent() const{ + + if (!data_at_size.is_valid()) + return 1; + + return data_at_size->get_descent(); + +} + +Size2 DynamicFont::get_char_size(CharType p_char,CharType p_next) const{ + + if (!data_at_size.is_valid()) + return Size2(1,1); + + return data_at_size->get_char_size(p_char,p_next); + +} + +bool DynamicFont::is_distance_field_hint() const{ + + return false; +} + +float DynamicFont::draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next,const Color& p_modulate) const { + + if (!data_at_size.is_valid()) + return 0; + + return data_at_size->draw_char(p_canvas_item,p_pos,p_char,p_next,p_modulate); + +} + +DynamicFont::DynamicFont() { + + size=16; +} + +DynamicFont::~DynamicFont() { + +} + +///////////////////////// + + +RES ResourceFormatLoaderDynamicFont::load(const String &p_path, const String& p_original_path, Error *r_error) { + + if (r_error) + *r_error=ERR_FILE_CANT_OPEN; + + + FileAccess *f = FileAccess::open(p_path,FileAccess::READ); + ERR_FAIL_COND_V(!f,RES()); + + DVector<uint8_t> data; + + data.resize(f->get_len()); + + ERR_FAIL_COND_V(data.size()==0,RES()); + + { + DVector<uint8_t>::Write w = data.write(); + f->get_buffer(w.ptr(),data.size()); + } + + Ref<DynamicFontData> dfd; + dfd.instance(); + dfd->set_font_data(data); + + if (r_error) + *r_error=OK; + + return dfd; +} + +void ResourceFormatLoaderDynamicFont::get_recognized_extensions(List<String> *p_extensions) const { + + p_extensions->push_back("ttf"); +} + +bool ResourceFormatLoaderDynamicFont::handles_type(const String& p_type) const { + + return (p_type=="DynamicFontData"); +} + +String ResourceFormatLoaderDynamicFont::get_resource_type(const String &p_path) const { + + String el = p_path.extension().to_lower(); + if (el=="ttf") + return "DynamicFontData"; + return ""; +} + +#endif diff --git a/scene/resources/dynamic_font_stb.h b/scene/resources/dynamic_font_stb.h new file mode 100644 index 0000000000..6b72fb3703 --- /dev/null +++ b/scene/resources/dynamic_font_stb.h @@ -0,0 +1,178 @@ +#ifndef DYNAMICFONT_STB_H +#define DYNAMICFONT_STB_H + +#ifndef FREETYPE_ENABLED + +#include "font.h" +#include "stb_truetype.h" +#include "io/resource_loader.h" + + + +class DynamicFontAtSize; +class DynamicFont; + +class DynamicFontData : public Resource { + + OBJ_TYPE(DynamicFontData,Resource); + + bool valid; + + DVector<uint8_t> font_data; + DVector<uint8_t>::Read fr; + const uint8_t* last_data_ptr; + + struct KerningPairKey { + + union { + struct { + uint32_t A,B; + }; + + uint64_t pair; + }; + + _FORCE_INLINE_ bool operator<(const KerningPairKey& p_r) const { return pair<p_r.pair; } + }; + + Map<KerningPairKey,int> kerning_map; + + + Map<int,DynamicFontAtSize*> size_cache; + +friend class DynamicFontAtSize; + + stbtt_fontinfo info; + int ascent; + int descent; + int linegap; + + void lock(); + void unlock(); + +friend class DynamicFont; + + + Ref<DynamicFontAtSize> _get_dynamic_font_at_size(int p_size); +public: + + void set_font_data(const DVector<uint8_t>& p_font); + DynamicFontData(); + ~DynamicFontData(); +}; + + +class DynamicFontAtSize : public Reference { + + OBJ_TYPE(DynamicFontAtSize,Reference); + + + int rect_margin; + + struct CharTexture { + + DVector<uint8_t> imgdata; + int texture_size; + Vector<int> offsets; + Ref<ImageTexture> texture; + }; + + Vector<CharTexture> textures; + + struct Character { + + int texture_idx; + Rect2 rect; + float v_align; + float h_align; + float advance; + + Character() { texture_idx=0; v_align=0; } + }; + + + + HashMap< CharType, Character > char_map; + + _FORCE_INLINE_ void _update_char(CharType p_char); + +friend class DynamicFontData; + Ref<DynamicFontData> font; + float scale; + int size; + +protected: + +public: + + float get_height() const; + + float get_ascent() const; + float get_descent() const; + + Size2 get_char_size(CharType p_char,CharType p_next=0) const; + + float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const; + + + + DynamicFontAtSize(); + ~DynamicFontAtSize(); +}; + +/////////////// + +class DynamicFont : public Font { + + OBJ_TYPE( DynamicFont, Font ); + + Ref<DynamicFontData> data; + Ref<DynamicFontAtSize> data_at_size; + int size; + + +protected: + + static void _bind_methods(); + +public: + + void set_font_data(const Ref<DynamicFontData>& p_data); + Ref<DynamicFontData> get_font_data() const; + + void set_size(int p_size); + int get_size() const; + + virtual float get_height() const; + + virtual float get_ascent() const; + virtual float get_descent() const; + + virtual Size2 get_char_size(CharType p_char,CharType p_next=0) const; + + virtual bool is_distance_field_hint() const; + + virtual float draw_char(RID p_canvas_item, const Point2& p_pos, const CharType& p_char,const CharType& p_next=0,const Color& p_modulate=Color(1,1,1)) const; + + DynamicFont(); + ~DynamicFont(); + +}; + + + +///////////// + +class ResourceFormatLoaderDynamicFont : public ResourceFormatLoader { +public: + + virtual RES load(const String &p_path,const String& p_original_path="",Error *r_error=NULL); + virtual void get_recognized_extensions(List<String> *p_extensions) const; + virtual bool handles_type(const String& p_type) const; + virtual String get_resource_type(const String &p_path) const; + +}; + + +#endif +#endif // DYNAMICFONT_H |