Mercurial > hg > forks > bilotrip-mj12
diff src/texture-font.c @ 0:785057719d9b
Import.
author | Matti Hamalainen <ccr@tnsp.org> |
---|---|
date | Mon, 05 Aug 2013 12:25:43 +0300 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/texture-font.c Mon Aug 05 12:25:43 2013 +0300 @@ -0,0 +1,638 @@ +/* =========================================================================== + * Freetype GL - A C OpenGL Freetype engine + * Platform: Any + * WWW: http://code.google.com/p/freetype-gl/ + * ---------------------------------------------------------------------------- + * Copyright 2011,2012 Nicolas P. Rougier. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY NICOLAS P. ROUGIER ''AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + * EVENT SHALL NICOLAS P. ROUGIER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Nicolas P. Rougier. + * ============================================================================ + */ +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_STROKER_H +// #include FT_ADVANCES_H +#include FT_LCD_FILTER_H +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <assert.h> +#include <math.h> +#include <wchar.h> +#include "platform.h" +#include "texture-font.h" + +#undef __FTERRORS_H__ +#define FT_ERRORDEF( e, v, s ) { e, s }, +#define FT_ERROR_START_LIST { +#define FT_ERROR_END_LIST { 0, 0 } }; +const struct { + int code; + const char* message; +} FT_Errors[] = +#include FT_ERRORS_H + + + + +// ------------------------------------------------- texture_font_load_face --- +int +texture_font_load_face( FT_Library * library, + const char * filename, + const float size, + FT_Face * face ) +{ + size_t hres = 64; + FT_Error error; + FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L), + (int)((0.0) * 0x10000L), + (int)((0.0) * 0x10000L), + (int)((1.0) * 0x10000L) }; + + assert( library ); + assert( filename ); + assert( size ); + + /* Initialize library */ + error = FT_Init_FreeType( library ); + if( error ) + { + fprintf(stderr, "FT_Error (0x%02x) : %s\n", + FT_Errors[error].code, FT_Errors[error].message); + return 0; + } + + /* Load face */ + error = FT_New_Face( *library, filename, 0, face ); + 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 0; + } + + /* Select charmap */ + error = FT_Select_Charmap( *face, FT_ENCODING_UNICODE ); + if( error ) + { + fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n", + __LINE__, FT_Errors[error].code, FT_Errors[error].message ); + FT_Done_Face( *face ); + FT_Done_FreeType( *library ); + return 0; + } + + /* Set char size */ + error = FT_Set_Char_Size( *face, (int)(size*64), 0, 72*hres, 72 ); + if( error ) + { + fprintf( stderr, "FT_Error (line %d, code 0x%02x) : %s\n", + __LINE__, FT_Errors[error].code, FT_Errors[error].message ); + FT_Done_Face( *face ); + FT_Done_FreeType( *library ); + return 0; + } + + /* Set transform matrix */ + FT_Set_Transform( *face, &matrix, NULL ); + + return 1; +} + + +// ------------------------------------------------------ texture_glyph_new --- +texture_glyph_t * +texture_glyph_new( void ) +{ + texture_glyph_t *self = (texture_glyph_t *) malloc( sizeof(texture_glyph_t) ); + if( self == NULL) + { + fprintf( stderr, + "line %d: No more memory for allocating data\n", __LINE__ ); + exit( EXIT_FAILURE ); + } + self->id = 0; + self->width = 0; + self->height = 0; + self->outline_type = 0; + self->outline_thickness = 0.0; + self->offset_x = 0; + self->offset_y = 0; + self->advance_x = 0.0; + self->advance_y = 0.0; + self->s0 = 0.0; + self->t0 = 0.0; + self->s1 = 0.0; + self->t1 = 0.0; + self->kerning = vector_new( sizeof(kerning_t) ); + return self; +} + + +// --------------------------------------------------- texture_glyph_delete --- +void +texture_glyph_delete( texture_glyph_t *self ) +{ + assert( self ); + vector_delete( self->kerning ); + free( self ); +} + +// ---------------------------------------------- texture_glyph_get_kerning --- +float +texture_glyph_get_kerning( const texture_glyph_t * self, + const wchar_t charcode ) +{ + size_t i; + + assert( self ); + for( i=0; i<vector_size(self->kerning); ++i ) + { + kerning_t * kerning = (kerning_t *) vector_get( self->kerning, i ); + if( kerning->charcode == charcode ) + { + return kerning->kerning; + } + } + return 0; +} + + +// ------------------------------------------ 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; + } + + /* 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 ); +} + + +// ------------------------------------------------------- texture_font_new --- +texture_font_t * +texture_font_new( texture_atlas_t * atlas, + const char * filename, + const float size) +{ + texture_font_t *self = (texture_font_t *) malloc( sizeof(texture_font_t) ); + FT_Library library; + FT_Face face; + FT_Size_Metrics metrics; + + assert( filename ); + assert( size ); + + 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->kerning = 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 */ + + 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; + } + + 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; +} + + +// ---------------------------------------------------- texture_font_delete --- +void +texture_font_delete( texture_font_t *self ) +{ + size_t i; + texture_glyph_t *glyph; + + assert( self ); + + if( self->filename ) + { + free( self->filename ); + } + + + for( i=0; i<vector_size( self->glyphs ); ++i) + { + glyph = *(texture_glyph_t **) vector_get( self->glyphs, i ); + texture_glyph_delete( glyph); + } + + vector_delete( self->glyphs ); + free( self ); +} + + +// ----------------------------------------------- texture_font_load_glyphs --- +size_t +texture_font_load_glyphs( texture_font_t * self, + const wchar_t * 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; + + assert( self ); + assert( charcodes ); + + + 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 ) + { + FT_Int32 flags = 0; + 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; + glyph_index = FT_Get_Char_Index( face, charcodes[i] ); + // WARNING: We use texture-atlas depth to guess if user wants + // LCD subpixel rendering + + 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_Face( face ); + FT_Done_FreeType( library ); + return wcslen(charcodes)-i; + } + + + 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; + FT_BitmapGlyph ft_bitmap_glyph; + error = FT_Stroker_New( library, &stroker ); + if( error ) + { + fprintf(stderr, "FT_Error (0x%02x) : %s\n", + FT_Errors[error].code, FT_Errors[error].message); + FT_Done_Face( face ); + FT_Stroker_Done( stroker ); + FT_Done_FreeType( library ); + 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); + FT_Done_Face( face ); + FT_Stroker_Done( stroker ); + FT_Done_FreeType( library ); + 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); + FT_Done_Face( face ); + FT_Stroker_Done( stroker ); + FT_Done_FreeType( library ); + 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); + FT_Done_Face( face ); + FT_Stroker_Done( stroker ); + FT_Done_FreeType( library ); + 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); + FT_Done_Face( face ); + FT_Stroker_Done( stroker ); + FT_Done_FreeType( library ); + return 0; + } + } + 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; + 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; + } + w = w - 1; + h = h - 1; + x = region.x; + y = region.y; + 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; + + 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_get_glyph --- +texture_glyph_t * +texture_font_get_glyph( texture_font_t * self, + wchar_t charcode ) +{ + size_t i; + wchar_t buffer[2] = {0,0}; + texture_glyph_t *glyph; + + assert( self ); + + assert( self ); + assert( self->filename ); + assert( self->atlas ); + + /* Check if charcode has been already loaded */ + for( i=0; i<self->glyphs->size; ++i ) + { + glyph = *(texture_glyph_t **) vector_get( self->glyphs, i ); + // If charcode is -1, we don't care about outline type or thickness + if( (glyph->charcode == charcode) && + ((charcode == (wchar_t)(-1) ) || + ((glyph->outline_type == self->outline_type) && + (glyph->outline_thickness == self->outline_thickness)) )) + { + return glyph; + } + } + + /* charcode -1 is special : it is used for line drawing (overline, + * underline, strikethrough) and background. + */ + if( charcode == (wchar_t)(-1) ) + { + size_t width = self->atlas->width; + size_t height = self->atlas->height; + ivec4 region = texture_atlas_get_region( self->atlas, 5, 5 ); + texture_glyph_t * glyph = texture_glyph_new( ); + static unsigned char data[4*4*3] = {0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, + 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff}; + if ( region.x < 0 ) + { + fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); + return NULL; + } + texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 ); + glyph->charcode = (wchar_t)(-1); + glyph->s0 = (region.x+2)/(float)width; + glyph->t0 = (region.y+2)/(float)height; + glyph->s1 = (region.x+3)/(float)width; + glyph->t1 = (region.y+3)/(float)height; + vector_push_back( self->glyphs, &glyph ); + return glyph; //*(texture_glyph_t **) vector_back( self->glyphs ); + } + + /* Glyph has not been already loaded */ + buffer[0] = charcode; + if( texture_font_load_glyphs( self, buffer ) == 0 ) + { + return *(texture_glyph_t **) vector_back( self->glyphs ); + } + return NULL; +} + +