// Copyright (C) 2012-2015 ChaosForge Ltd // http://chaosforge.org/ // // This file is part of Nova libraries. // For conditions of distribution and use, see copying.txt file in root folder. #include "nv/gfx/texture_font.hh" #include "nv/lib/freetype2.hh" #include "nv/core/logging.hh" using namespace nv; #define NV_CHECK_FREETYPE_ERROR( error, ... ) \ if ( error != 0 ) { \ NV_LOG_CRITICAL( "freetype : ", __VA_ARGS__ ); \ NV_ABORT( "freetype : freetype library error!" ); \ } texture_glyph::texture_glyph() : charcode(0), size(0,0), offset(0,0), advance(0.f,0.f), tl(0.f,0.f), br(0.f,0.f) { } float texture_glyph::get_kerning( const uint16 other ) { auto i = kerning.find( other ); return i != kerning.end() ? i->second : 0.0f; } texture_font::texture_font( texture_atlas* atlas, const char * filename, float size ) : m_atlas( atlas ), m_filename(filename), m_size( size ), m_height(0), m_linegap(0), m_ascender(0), m_descender(0), m_hinting( true ), m_filtering( true ), m_rlibrary( nullptr ), m_rface( nullptr ) { load_freetype_library(); size_t hres = 64; FT_Error error; FT_Matrix matrix = { static_cast((1.0/hres) * 0x10000L), static_cast((0.0) * 0x10000L), static_cast((0.0) * 0x10000L), static_cast((1.0) * 0x10000L) }; m_lcd_weights[0] = 0x10; m_lcd_weights[1] = 0x40; m_lcd_weights[2] = 0x70; m_lcd_weights[3] = 0x40; m_lcd_weights[4] = 0x10; error = FT_Init_FreeType( reinterpret_cast(&m_rlibrary) ); NV_CHECK_FREETYPE_ERROR( error, "error on FT_Init_FreeType, code - ", error ); error = FT_New_Face( reinterpret_cast(m_rlibrary), filename, 0, reinterpret_cast(&m_rface) ); NV_CHECK_FREETYPE_ERROR( error, "error on FT_New_Face, code - ", error ); error = FT_Set_Char_Size( reinterpret_cast(m_rface), int(size*64), 0, 72*64, 72 ); NV_CHECK_FREETYPE_ERROR( error, "error on FT_Set_Char_Size, code - ", error ); FT_Set_Transform( reinterpret_cast(m_rface), &matrix, NULL ); FT_Size_Metrics metrics = reinterpret_cast(m_rface)->size->metrics; m_ascender = static_cast(metrics.ascender >> 6) / 100.0f; m_descender = static_cast(metrics.descender >> 6) / 100.0f; m_height = static_cast(metrics.height >> 6) / 100.0f; m_linegap = m_height - m_ascender + m_descender; } const texture_glyph* texture_font::get_glyph( uint16 charcode ) const { auto i = m_glyphs.find( charcode ); if ( i == m_glyphs.end() ) { return nullptr; } return &(i->second); } static uint8* convert_to_rgba(uint8* rgb, const int lines, const int line_count, const int line_bpitch ) { uint8* result = new uint8[ lines * line_count * 4 ]; uint8* rgba = result; for(int l=0; l( m_rface ); size_t depth = m_atlas->get_depth(); vec2 asize = vec2( m_atlas->get_size() ); FT_Int32 flags = 0; flags |= FT_LOAD_RENDER; if( !m_hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( m_atlas->get_depth() >= 3 ) { FT_Library_SetLcdFilter( reinterpret_cast( m_rlibrary ), FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if ( m_filtering ) { FT_Library_SetLcdFilterWeights( reinterpret_cast( m_rlibrary ), m_lcd_weights ); } } for ( char ch : codes ) { uint16 c = static_cast( ch ); FT_UInt glyph_index = FT_Get_Char_Index( face, c ); FT_Error error = FT_Load_Glyph( face, glyph_index, flags ); NV_CHECK_FREETYPE_ERROR( error, "error on FT_Load_Glyph, gylph '", c ,"' code - ", error ); FT_GlyphSlot slot = face->glyph; FT_Bitmap ft_bitmap = slot->bitmap; int ft_bitmap_width = slot->bitmap.width; int ft_bitmap_rows = slot->bitmap.rows; int ft_glyph_top = slot->bitmap_top; int ft_glyph_left = slot->bitmap_left; int reg_width = ft_bitmap_width / (depth > 3 ? 3 : int( depth ) ); ivec2 gsize( reg_width + 1, ft_bitmap_rows + 1 ); region r = m_atlas->get_region( gsize ); if ( r.pos.x < 0 ) { NV_LOG_CRITICAL( "texture_font : atlas full while loading glyphs, gylph '", c, "' r.pos.x = ", r.pos.x ); NV_ABORT( "texture_font : atlas full while loading gylphs!" ); } if (depth == 4) { r.size.x -= 1; r.size.y -= 1; uint8* data = convert_to_rgba(ft_bitmap.buffer, r.size.y, r.size.x, ft_bitmap.pitch ); m_atlas->set_region( r, data ); delete data; } else { r.size.x -= 1; r.size.y -= 1; m_atlas->set_region( r, ft_bitmap.buffer, ft_bitmap.pitch ); } texture_glyph* g = &( m_glyphs[c] ); g->charcode = c; g->size = gsize; g->offset = ivec2( ft_glyph_left, ft_glyph_top ); g->tl = vec2( r.pos ) / asize; g->br = vec2( r.pos + gsize ) / asize; // Discard hinting to get advance FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; g->advance = ivec2( slot->advance.x/64.0f, slot->advance.y/64.0f ); } generate_kerning(); return true; } texture_font::~texture_font() { if ( m_rface ) FT_Done_Face( reinterpret_cast( m_rface ) ); if ( m_rlibrary ) FT_Done_FreeType( reinterpret_cast( m_rlibrary ) ); } void texture_font::generate_kerning() { }