// ------------------------------------------ texture_font_generate_kerning --- void texture_font_generate_kerning( texture_font_t *self ) { size_t i, j; FT_Library library; FT_Face face; FT_UInt glyph_index, prev_index; texture_glyph_t *glyph, *prev_glyph; FT_Vector kerning; assert( self ); /* Load font */ // if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) // { // return; // } if( !texture_font_load_face( self, &library, self->size, &face ) )// <----- changed { return; } /* For each glyph couple combination, check if kerning is necessary */ /* Starts at index 1 since 0 is for the special backgroudn glyph */ for( i=1; i<self->glyphs->size; ++i ) { glyph = *(texture_glyph_t **) vector_get( self->glyphs, i ); glyph_index = FT_Get_Char_Index( face, glyph->charcode ); vector_clear( glyph->kerning ); for( j=1; j<self->glyphs->size; ++j ) { prev_glyph = *(texture_glyph_t **) vector_get( self->glyphs, j ); prev_index = FT_Get_Char_Index( face, prev_glyph->charcode ); FT_Get_Kerning( face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning ); // printf("%c(%d)-%c(%d): %ld\n", // prev_glyph->charcode, prev_glyph->charcode, // glyph_index, glyph_index, kerning.x); if( kerning.x ) { // 64 * 64 because of 26.6 encoding AND the transform matrix used // in texture_font_load_face (hres = 64) kerning_t k = {prev_glyph->charcode, kerning.x / (float)(64.0f*64.0f)}; vector_push_back( glyph->kerning, &k ); } } } FT_Done_Face( face ); FT_Done_FreeType( library ); }
/* ------------------------------------------------------------------------- */ TextureFont * texture_font_new( TextureAtlas *atlas, const char *filename, const float size ) { TextureFont *self = (TextureFont *) malloc( sizeof(TextureFont) ); if( !self ) { return NULL; } self->glyphs = vector_new( sizeof(TextureGlyph) ); self->filename = strdup( filename ); self->size = size; self->gamma = 1.; self->atlas = atlas; self->height = 0; self->ascender = 0; self->descender = 0; self->hinting = 1; self->lcd_filter = 0; self->lcd_weights[0] = 0; self->lcd_weights[1] = 0; self->lcd_weights[2] = 255; self->lcd_weights[3] = 0; self->lcd_weights[4] = 0; /* Get font metrics at high resolution */ FT_Library library; FT_Face face; if( !texture_font_load_face( &library, self->filename, self->size*100, &face ) ) { return self; } FT_Size_Metrics metrics = face->size->metrics; self->ascender = (metrics.ascender >> 6) / 100.0; self->descender = (metrics.descender >> 6) / 100.0; self->height = (metrics.height >> 6) / 100.0; self->linegap = self->height - self->ascender + self->descender; return self; }
// ----------------------------------------------- texture_font_load_glyphs --- size_t texture_font_load_glyphs( texture_font_t * self, const wchar_t * charcodes ) { assert( self ); assert( charcodes ); size_t i, x, y, width, height, depth, w, h; FT_Library library; FT_Error error; FT_Face face; FT_Glyph ft_glyph; FT_GlyphSlot slot; FT_Bitmap ft_bitmap; FT_UInt glyph_index; texture_glyph_t *glyph; ivec4 region; size_t missed = 0; width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; region.x=1; region.y=1; #ifdef RENDERSTRING stringwidth=0; stringheight=0; stringshift=0; #endif if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) { return wcslen(charcodes); } /* Load each glyph */ for( i=0; i<wcslen(charcodes); ++i ) { glyph_index = FT_Get_Char_Index( face, charcodes[i] ); // WARNING: We use texture-atlas depth to guess if user wants // LCD subpixel rendering FT_Int32 flags = 0; if( self->outline_type > 0 ) { flags |= FT_LOAD_NO_BITMAP; } else { flags |= FT_LOAD_RENDER; } if( !self->hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( depth == 3 ) { FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if( self->filtering ) { // FT_Library_SetLcdFilterWeights( library, self->lcd_weights ); } } error = FT_Load_Glyph( face, glyph_index, flags ); if( error ) { fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n", __LINE__, FT_Errors[error].code, FT_Errors[error].message ); FT_Done_FreeType( library ); return wcslen(charcodes)-i; } int ft_bitmap_width = 0; int ft_bitmap_rows = 0; int ft_bitmap_pitch = 0; int ft_glyph_top = 0; int ft_glyph_left = 0; if( self->outline_type == 0 ) { slot = face->glyph; ft_bitmap = slot->bitmap; ft_bitmap_width = slot->bitmap.width; ft_bitmap_rows = slot->bitmap.rows; ft_bitmap_pitch = slot->bitmap.pitch; ft_glyph_top = slot->bitmap_top; ft_glyph_left = slot->bitmap_left; } else { FT_Stroker stroker; error = FT_Stroker_New( library, &stroker ); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } FT_Stroker_Set( stroker, (int)(self->outline_thickness *64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); error = FT_Get_Glyph( face->glyph, &ft_glyph); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( self->outline_type == 1 ) { error = FT_Glyph_Stroke( &ft_glyph, stroker, 1 ); } else if ( self->outline_type == 2 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 0, 1 ); } else if ( self->outline_type == 3 ) { error = FT_Glyph_StrokeBorder( &ft_glyph, stroker, 1, 1 ); } if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } if( depth == 1) { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } else { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); if( error ) { fprintf(stderr, "FT_Error (0x%02x) : %s\n", FT_Errors[error].code, FT_Errors[error].message); return 0; } } FT_BitmapGlyph ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph; ft_bitmap = ft_bitmap_glyph->bitmap; ft_bitmap_width = ft_bitmap.width; ft_bitmap_rows = ft_bitmap.rows; ft_bitmap_pitch = ft_bitmap.pitch; ft_glyph_top = ft_bitmap_glyph->top; ft_glyph_left = ft_bitmap_glyph->left; FT_Stroker_Done(stroker); } // We want each glyph to be separated by at least one black pixel // (for example for shader used in demo-subpixel.c) w = ft_bitmap_width/depth + 1; h = ft_bitmap_rows + 1; #ifdef RENDERSTRING static size_t maxh=0; if (charcodes[i]==13) { stringshift+=maxh+1; region.x=1; } if (h>maxh) maxh=h; //region.y=stringshift+maxh-h+1+(h-ft_glyph_top); region.y=stringshift+maxh-ft_glyph_top+1; // if (stringshift+h+h-ft_glyph_top+1>stringheight) stringheight=stringshift+h+h-ft_glyph_top+1; if (region.y+h>stringheight) stringheight=region.y+h; if (region.x+w>stringwidth) stringwidth=region.x+w; // if (region.y>=height) { missed++; continue; } // if (region.x+w>=width) {missed++; continue; } // if (h+h-ft_glyph_top+1>=height) { missed++; continue; } // if (h+h-ft_glyph_top+2>=height) { missed++; continue; } // if (region.y+maxh>=height) { missed++; continue; } #else region = texture_atlas_get_region( self->atlas, w, h ); if ( region.x < 0 ) { missed++; // fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } #endif w = w - 1; h = h - 1; x = region.x; y = region.y; if (charcodes[i]!=13) { #ifdef RENDERSTRING if (!(x<width)||!((x+w)<=width)||!(y<height)||!((y+h)<=height)) { missed++; continue; } #endif texture_atlas_set_region( self->atlas, x, y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); } glyph = texture_glyph_new( ); glyph->charcode = charcodes[i]; glyph->width = w; glyph->height = h; glyph->outline_type = self->outline_type; glyph->outline_thickness = self->outline_thickness; glyph->offset_x = ft_glyph_left; glyph->offset_y = ft_glyph_top; glyph->s0 = x/(float)width; glyph->t0 = y/(float)height; glyph->s1 = (x + glyph->width)/(float)width; glyph->t1 = (y + glyph->height)/(float)height; // Discard hinting to get advance FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; glyph->advance_x = slot->advance.x/64.0; glyph->advance_y = slot->advance.y/64.0; #ifdef RENDERSTRING if (charcodes[i]!=13) region.x+=(int)glyph->advance_x; #endif vector_push_back( self->glyphs, &glyph ); } if( self->outline_type > 0 ) { FT_Done_Glyph( ft_glyph ); } FT_Done_Face( face ); FT_Done_FreeType( library ); // texture_atlas_upload( self->atlas ); texture_font_generate_kerning( self ); return missed; }
// ------------------------------------------------------- texture_font_new --- texture_font_t * texture_font_new( texture_atlas_t * atlas, const char * filename, const float size) { assert( filename ); assert( size ); texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) ); if( self == NULL) { fprintf( stderr, "line %d: No more memory for allocating data\n", __LINE__ ); exit( EXIT_FAILURE ); } self->glyphs = vector_new( sizeof(texture_glyph_t *) ); self->atlas = atlas; self->height = 0; self->ascender = 0; self->descender = 0; self->filename = strdup( filename ); self->size = size; self->outline_type = 0; self->outline_thickness = 0.0; self->hinting = 1; self->filtering = 1; // FT_LCD_FILTER_LIGHT is (0x00, 0x55, 0x56, 0x55, 0x00) // FT_LCD_FILTER_DEFAULT is (0x10, 0x40, 0x70, 0x40, 0x10) self->lcd_weights[0] = 0x10; self->lcd_weights[1] = 0x40; self->lcd_weights[2] = 0x70; self->lcd_weights[3] = 0x40; self->lcd_weights[4] = 0x10; /* Get font metrics at high resolution */ FT_Library library; FT_Face face; if( !texture_font_load_face( &library, self->filename, self->size*100, &face ) ) { return self; } // 64 * 64 because of 26.6 encoding AND the transform matrix used // in texture_font_load_face (hres = 64) self->underline_position = face->underline_position / (float)(64.0f*64.0f) * self->size; self->underline_position = round( self->underline_position ); if( self->underline_position > -2 ) { self->underline_position = -2.0; } self->underline_thickness = face->underline_thickness / (float)(64.0f*64.0f) * self->size; self->underline_thickness = round( self->underline_thickness ); if( self->underline_thickness < 1 ) { self->underline_thickness = 1.0; } FT_Size_Metrics metrics = face->size->metrics; self->ascender = (metrics.ascender >> 6) / 100.0; self->descender = (metrics.descender >> 6) / 100.0; self->height = (metrics.height >> 6) / 100.0; self->linegap = self->height - self->ascender + self->descender; FT_Done_Face( face ); FT_Done_FreeType( library ); /* -1 is a special glyph */ texture_font_get_glyph( self, -1 ); return self; }
/* ------------------------------------------------------------------------- */ size_t texture_font_cache_glyphs( TextureFont *self, wchar_t * charcodes ) { size_t i, x, y, width, height, depth, w, h; FT_Library library; FT_Error error; FT_Face face; FT_GlyphSlot slot; FT_UInt glyph_index; TextureGlyph *glyph; Region region; unsigned char c; size_t missed = 0; width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) { return wcslen(charcodes); } /* Load each glyph */ for( i=0; i<wcslen(charcodes); ++i ) { glyph_index = FT_Get_Char_Index( face, charcodes[i] ); // WARNING: We use texture-atlas depth to guess if user wants // LCD subpixel rendering FT_Int32 flags = FT_LOAD_RENDER; if( !self->hinting ) { flags |= FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT; } else { flags |= FT_LOAD_FORCE_AUTOHINT; } if( depth == 3 ) { FT_Library_SetLcdFilter( library, FT_LCD_FILTER_LIGHT ); flags |= FT_LOAD_TARGET_LCD; if( self->lcd_filter ) { FT_Library_SetLcdFilterWeights( library, self->lcd_weights ); } } error = FT_Load_Glyph( face, glyph_index, flags ); if( error ) { fprintf(stderr, "FT_Error (line %d, code 0x%02x) : %s\n", __LINE__, FT_Errors[error].code, FT_Errors[error].message); FT_Done_FreeType( library ); return wcslen(charcodes)-i; } slot = face->glyph; /* Gamma correction (sort of) */ for( x=0; x<slot->bitmap.width; ++x ) { for( y=0; y<slot->bitmap.rows; ++y ) { c = *(unsigned char *)(slot->bitmap.buffer + y*slot->bitmap.pitch + x ); c = (unsigned char) ( pow(c/255.0, self->gamma) * 255); *(unsigned char *)(slot->bitmap.buffer + y*slot->bitmap.pitch + x ) = c; } } // We want each glyph to be separated by at least one black pixel // (for example for shader used in demo-subpixel.c) w = slot->bitmap.width/depth + 1; h = slot->bitmap.rows + 1; region = texture_atlas_get_region( self->atlas, w, h ); if ( region.x < 0 ) { missed++; continue; } w = w - 1; h = h - 1; x = region.x; y = region.y; texture_atlas_set_region( self->atlas, x, y, w, h, slot->bitmap.buffer, slot->bitmap.pitch ); glyph = texture_glyph_new( ); glyph->font = self; glyph->charcode = charcodes[i]; glyph->kerning = 0; glyph->width = w; glyph->height = h; glyph->offset_x = slot->bitmap_left; glyph->offset_y = slot->bitmap_top; glyph->u0 = x/(float)width; glyph->v0 = y/(float)height; glyph->u1 = (x + glyph->width)/(float)width; glyph->v1 = (y + glyph->height)/(float)height; /* Discard hinting to get advance */ FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING); slot = face->glyph; glyph->advance_x = slot->advance.x/64.0; glyph->advance_y = slot->advance.y/64.0; vector_push_back( self->glyphs, glyph ); texture_glyph_delete( glyph ); } FT_Done_Face( face ); FT_Done_FreeType( library ); texture_atlas_upload( self->atlas ); texture_font_generate_kerning( self ); return missed; }
/* ------------------------------------------------------------------------- */ void texture_font_generate_kerning( TextureFont *self ) { size_t i, j, k, count; FT_Library library; FT_Face face; FT_UInt glyph_index, prev_index; TextureGlyph *glyph, *prev_glyph; FT_Vector kerning; /* Load font */ if( !texture_font_load_face( &library, self->filename, self->size, &face ) ) { return; } /* For each glyph couple combination, check if kerning is necessary */ for( i=0; i<self->glyphs->size; ++i ) { glyph = (TextureGlyph *) vector_get( self->glyphs, i ); /* Remove any old kerning information */ if( glyph->kerning ) { free( glyph->kerning ); glyph->kerning = 0; glyph->kerning_count = 0; } /* Count how many kerning pairs we need */ count = 0; glyph_index = FT_Get_Char_Index( face, glyph->charcode ); for( j=0; j<self->glyphs->size; ++j ) { prev_glyph = (TextureGlyph *) vector_get( self->glyphs, j ); prev_index = FT_Get_Char_Index( face, prev_glyph->charcode ); FT_Get_Kerning( face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning ); if( kerning.x != 0.0 ) { count++; } } /* No kerning at all */ if( !count ) { continue; } /* Store kerning pairs */ glyph->kerning = (KerningPair *) malloc( count * sizeof(KerningPair) ); glyph->kerning_count = count; k = 0; for( j=0; j<self->glyphs->size; ++j ) { prev_glyph = (TextureGlyph *) vector_get( self->glyphs, j ); prev_index = FT_Get_Char_Index( face, prev_glyph->charcode ); FT_Get_Kerning( face, prev_index, glyph_index, FT_KERNING_UNFITTED, &kerning ); if( kerning.x != 0.0 ) { glyph->kerning[k].charcode = prev_glyph->charcode; // 64 * 64 because of 26.6 encoding AND the transform matrix used // in texture_font_load_face (hres = 64) glyph->kerning[k].kerning = kerning.x/ (float)(64.0f*64.0f); ++k; } } } FT_Done_Face( face ); FT_Done_FreeType( library ); }
static int texture_font_get_face_with_size(texture_font_t *self, float size, FT_Library *library, FT_Face *face) { return texture_font_load_face(self, size, library, face); }