/* ------------------------------------------------------------------------- */ void texture_atlas_clear( TextureAtlas *self ) { vector_clear( self->nodes ); self->used = 0; { Node node = {0,0,self->width}; vector_push_back( self->nodes, &node ); memset( self->data, 0, self->width*self->height*self->depth ); { // This is a special region that is used for background and underlined // decorations of glyphs int n = 4; //unsigned char buffer[n*n]; unsigned char buffer[16]; memset(buffer, 255, n*n); { Region r = texture_atlas_get_region( self, n, n ); texture_atlas_set_region( self, r.x, r.y, r.width, r.height, buffer, 1); self->black.x = r.x + 1; self->black.y = r.y + 1; self->black.width = r.width - 2; self->black.height= r.height - 2; } } } }
/* ------------------------------------------------------------------------- */ TextureAtlas * texture_atlas_new( size_t width, size_t height, size_t depth ) { assert( (depth == 1) || (depth == 3) ); TextureAtlas *self = (TextureAtlas *) malloc( sizeof(TextureAtlas) ); if( !self ) { return NULL; } self->nodes = vector_new( sizeof(Node) ); self->used = 0; self->width = width; self->height = height; self->depth = depth; Node node = {0,0,width}; vector_push_back( self->nodes, &node ); self->texid = 0; self->data = (unsigned char *) calloc( width*height*depth, sizeof(unsigned char) ); // This is a special region that is used for background and underlined // decorations of glyphs int n = 4; unsigned char buffer[n*n]; memset(buffer, 255, n*n); Region1 r = texture_atlas_get_region( self, n, n ); texture_atlas_set_region( self, r.x, r.y, r.width, r.height, buffer, 1); self->black.x = r.x + 1; self->black.y = r.y + 1; self->black.width = r.width - 2; self->black.height= r.height - 2; return self; }
// ------------------------------------------------- texture_font_get_glyph --- texture_glyph_t * texture_font_get_glyph( texture_font_t * self, wchar_t charcode ) { assert( self ); size_t i; static wchar_t *buffer = 0; texture_glyph_t *glyph; 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( (glyph->charcode == charcode) && (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] = {-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1}; if ( region.x < 0 ) { // fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); return NULL; } #ifndef RENDERSTRING texture_atlas_set_region( self->atlas, region.x, region.y, 4, 4, data, 0 ); #else data[0]=0; #endif 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 */ if( !buffer) { buffer = (wchar_t *) calloc( 2, sizeof(wchar_t) ); } buffer[0] = charcode; if( texture_font_load_glyphs( self, buffer ) == 0 ) { return *(texture_glyph_t **) vector_back( self->glyphs ); } return NULL; }
// ----------------------------------------------- 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; }
// ------------------------------------------------------------- load_glyph --- texture_glyph_t * load_glyph( const char * filename, const wchar_t charcode, const float highres_size, const float lowres_size, const float padding ) { size_t i, j; FT_Library library; FT_Face face; FT_Init_FreeType( &library ); FT_New_Face( library, filename, 0, &face ); FT_Select_Charmap( face, FT_ENCODING_UNICODE ); FT_UInt glyph_index = FT_Get_Char_Index( face, charcode ); // Render glyph at high resolution (highres_size points) FT_Set_Char_Size( face, highres_size*64, 0, 72, 72 ); FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT); FT_GlyphSlot slot = face->glyph; FT_Bitmap bitmap = slot->bitmap; // Allocate high resolution buffer size_t highres_width = bitmap.width + 2*padding*highres_size; size_t highres_height = bitmap.rows + 2*padding*highres_size; double * highres_data = (double *) malloc( highres_width*highres_height*sizeof(double) ); memset( highres_data, 0, highres_width*highres_height*sizeof(double) ); // Copy high resolution bitmap with padding and normalize values for( j=0; j < bitmap.rows; ++j ) { for( i=0; i < bitmap.width; ++i ) { int x = i + padding; int y = j + padding; highres_data[y*highres_width+x] = bitmap.buffer[j*bitmap.width+i]/255.0; } } // Compute distance map distance_map( highres_data, highres_width, highres_height ); // Allocate low resolution buffer size_t lowres_width = round(highres_width * lowres_size/highres_size); size_t lowres_height = round(highres_height * lowres_width/(float) highres_width); double * lowres_data = (double *) malloc( lowres_width*lowres_height*sizeof(double) ); memset( lowres_data, 0, lowres_width*lowres_height*sizeof(double) ); // Scale down highres buffer into lowres buffer resize( highres_data, highres_width, highres_height, lowres_data, lowres_width, lowres_height ); // Convert the (double *) lowres buffer into a (unsigned char *) buffer and // rescale values between 0 and 255. unsigned char * data = (unsigned char *) malloc( lowres_width*lowres_height*sizeof(unsigned char) ); for( j=0; j < lowres_height; ++j ) { for( i=0; i < lowres_width; ++i ) { double v = lowres_data[j*lowres_width+i]; data[j*lowres_width+i] = (int) (255*(1-v)); } } // Compute new glyph information from highres value float ratio = lowres_size / highres_size; size_t pitch = lowres_width * sizeof( unsigned char ); // Create glyph texture_glyph_t * glyph = texture_glyph_new( ); glyph->offset_x = (slot->bitmap_left + padding*highres_width) * ratio; glyph->offset_y = (slot->bitmap_top + padding*highres_height) * ratio; glyph->width = lowres_width; glyph->height = lowres_height; glyph->charcode = charcode; /* printf( "Glyph width: %ld\n", glyph->width ); printf( "Glyph height: %ld\n", glyph->height ); printf( "Glyph offset x: %d\n", glyph->offset_x ); printf( "Glyph offset y: %d\n", glyph->offset_y ); */ ivec4 region = texture_atlas_get_region( atlas, glyph->width, glyph->height ); /* printf( "Region x : %d\n", region.x ); printf( "Region y : %d\n", region.y ); printf( "Region width : %d\n", region.width ); printf( "Region height : %d\n", region.height ); */ texture_atlas_set_region( atlas, region.x, region.y, glyph->width, glyph->height, data, pitch ); glyph->s0 = region.x/(float)atlas->width; glyph->t0 = region.y/(float)atlas->height; glyph->s1 = (region.x + glyph->width)/(float)atlas->width; glyph->t1 = (region.y + glyph->height)/(float)atlas->height; FT_Load_Glyph( face, glyph_index, FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT); glyph->advance_x = ratio * face->glyph->advance.x/64.0; glyph->advance_y = ratio * face->glyph->advance.y/64.0; /* printf( "Advance x : %f\n", glyph->advance_x ); printf( "Advance y : %f\n", glyph->advance_y ); */ free( highres_data ); free( lowres_data ); free( data ); return glyph; }
/* ------------------------------------------------------------------------- */ 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; }
// ------------------------------------------------------------------- init --- void init( void ) { size_t i, j; int ptSize = 50*64; int device_hdpi = 72; int device_vdpi = 72; atlas = texture_atlas_new( 512, 512, 3 ); /* Init freetype */ FT_Library ft_library; assert(!FT_Init_FreeType(&ft_library)); /* Load our fonts */ FT_Face ft_face[NUM_EXAMPLES]; assert(!FT_New_Face( ft_library, fonts[ENGLISH], 0, &ft_face[ENGLISH]) ); assert(!FT_Set_Char_Size( ft_face[ENGLISH], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[ENGLISH] ); // wonderful world of encodings ... force_ucs2_charmap( ft_face[ENGLISH] ); // which we ignore. assert( !FT_New_Face(ft_library, fonts[ARABIC], 0, &ft_face[ARABIC]) ); assert( !FT_Set_Char_Size(ft_face[ARABIC], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[ARABIC] ); force_ucs2_charmap( ft_face[ARABIC] ); assert(!FT_New_Face( ft_library, fonts[CHINESE], 0, &ft_face[CHINESE]) ); assert(!FT_Set_Char_Size( ft_face[CHINESE], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[CHINESE] ); force_ucs2_charmap( ft_face[CHINESE] ); /* Get our harfbuzz font structs */ hb_font_t *hb_ft_font[NUM_EXAMPLES]; hb_ft_font[ENGLISH] = hb_ft_font_create( ft_face[ENGLISH], NULL ); hb_ft_font[ARABIC] = hb_ft_font_create( ft_face[ARABIC] , NULL ); hb_ft_font[CHINESE] = hb_ft_font_create( ft_face[CHINESE], NULL ); /* Create a buffer for harfbuzz to use */ hb_buffer_t *buf = hb_buffer_create(); for (i=0; i < NUM_EXAMPLES; ++i) { hb_buffer_set_direction( buf, text_directions[i] ); /* or LTR */ hb_buffer_set_script( buf, scripts[i] ); /* see hb-unicode.h */ hb_buffer_set_language( buf, hb_language_from_string(languages[i], strlen(languages[i])) ); /* Layout the text */ hb_buffer_add_utf8( buf, texts[i], strlen(texts[i]), 0, strlen(texts[i]) ); hb_shape( hb_ft_font[i], buf, NULL, 0 ); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); FT_GlyphSlot slot; FT_Bitmap ft_bitmap; float size = 24; size_t hres = 64; FT_Error error; FT_Int32 flags = 0; flags |= FT_LOAD_RENDER; flags |= FT_LOAD_TARGET_LCD; FT_Library_SetLcdFilter( ft_library, FT_LCD_FILTER_LIGHT ); FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L), (int)((0.0) * 0x10000L), (int)((0.0) * 0x10000L), (int)((1.0) * 0x10000L) }; /* Set char size */ error = FT_Set_Char_Size( ft_face[i], (int)(ptSize), 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( ft_face[i] ); break; } /* Set transform matrix */ FT_Set_Transform( ft_face[i], &matrix, NULL ); for (j = 0; j < glyph_count; ++j) { /* Load glyph */ error = FT_Load_Glyph( ft_face[i], glyph_info[j].codepoint, 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( ft_face[i] ); break; } slot = ft_face[i]->glyph; ft_bitmap = slot->bitmap; int ft_bitmap_width = slot->bitmap.width; int ft_bitmap_rows = slot->bitmap.rows; int ft_bitmap_pitch = slot->bitmap.pitch; int ft_glyph_top = slot->bitmap_top; int ft_glyph_left = slot->bitmap_left; int w = ft_bitmap_width/3; // 3 because of LCD/RGB encoding int h = ft_bitmap_rows; ivec4 region = texture_atlas_get_region( atlas, w+1, h+1 ); if ( region.x < 0 ) { fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } int x = region.x, y = region.y; texture_atlas_set_region( atlas, region.x, region.y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); printf("%d: %dx%d %f %f\n", glyph_info[j].codepoint, ft_bitmap_width, ft_bitmap_rows, glyph_pos[j].x_advance/64., glyph_pos[j].y_advance/64.); } /* clean up the buffer, but don't kill it just yet */ hb_buffer_reset(buf); } /* Cleanup */ hb_buffer_destroy( buf ); for( i=0; i < NUM_EXAMPLES; ++i ) hb_font_destroy( hb_ft_font[i] ); FT_Done_FreeType( ft_library ); glClearColor(1,1,1,1); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glGenTextures( 1, &atlas->id ); glBindTexture( GL_TEXTURE_2D, atlas->id ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGB, atlas->width, atlas->height, 0, GL_RGB, GL_UNSIGNED_BYTE, atlas->data ); typedef struct { float x,y,z, u,v, r,g,b,a, shift, gamma; } vertex_t; vertex_t vertices[4] = { { 0, 0,0, 0,1, 0,0,0,1, 0, 1}, { 0,512,0, 0,0, 0,0,0,1, 0, 1}, {512,512,0, 1,0, 0,0,0,1, 0, 1}, {512, 0,0, 1,1, 0,0,0,1, 0, 1} }; GLuint indices[6] = { 0, 1, 2, 0,2,3 }; buffer = vertex_buffer_new( "vertex:3f," "tex_coord:2f," "color:4f," "ashift:1f," "agamma:1f" ); vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); shader = shader_load("shaders/text.vert", "shaders/text.frag"); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); }
// ------------------------------------------------------------------- main --- int main( int argc, char **argv ) { size_t i, j; int ptSize = 50*64; int device_hdpi = 72; int device_vdpi = 72; glutInit( &argc, argv ); glutInitWindowSize( 512, 512 ); glutInitDisplayMode( GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH ); glutCreateWindow( argv[0] ); glutReshapeFunc( reshape ); glutDisplayFunc( display ); glutKeyboardFunc( keyboard ); #ifndef __APPLE__ glewExperimental = GL_TRUE; GLenum err = glewInit(); if (GLEW_OK != err) { /* Problem: glewInit failed, something is seriously wrong. */ fprintf( stderr, "Error: %s\n", glewGetErrorString(err) ); exit( EXIT_FAILURE ); } fprintf( stderr, "Using GLEW %s\n", glewGetString(GLEW_VERSION) ); #endif texture_atlas_t * atlas = texture_atlas_new( 512, 512, 3 ); /* Init freetype */ FT_Library ft_library; assert(!FT_Init_FreeType(&ft_library)); /* Load our fonts */ FT_Face ft_face[NUM_EXAMPLES]; assert(!FT_New_Face( ft_library, fonts[ENGLISH], 0, &ft_face[ENGLISH]) ); assert(!FT_Set_Char_Size( ft_face[ENGLISH], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[ENGLISH] ); // wonderful world of encodings ... force_ucs2_charmap( ft_face[ENGLISH] ); // which we ignore. assert( !FT_New_Face(ft_library, fonts[ARABIC], 0, &ft_face[ARABIC]) ); assert( !FT_Set_Char_Size(ft_face[ARABIC], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[ARABIC] ); force_ucs2_charmap( ft_face[ARABIC] ); assert(!FT_New_Face( ft_library, fonts[CHINESE], 0, &ft_face[CHINESE]) ); assert(!FT_Set_Char_Size( ft_face[CHINESE], 0, ptSize, device_hdpi, device_vdpi ) ); // ftfdump( ft_face[CHINESE] ); force_ucs2_charmap( ft_face[CHINESE] ); /* Get our harfbuzz font structs */ hb_font_t *hb_ft_font[NUM_EXAMPLES]; hb_ft_font[ENGLISH] = hb_ft_font_create( ft_face[ENGLISH], NULL ); hb_ft_font[ARABIC] = hb_ft_font_create( ft_face[ARABIC] , NULL ); hb_ft_font[CHINESE] = hb_ft_font_create( ft_face[CHINESE], NULL ); /* Create a buffer for harfbuzz to use */ hb_buffer_t *buf = hb_buffer_create(); for (i=0; i < NUM_EXAMPLES; ++i) { hb_buffer_set_direction( buf, text_directions[i] ); /* or LTR */ hb_buffer_set_script( buf, scripts[i] ); /* see hb-unicode.h */ hb_buffer_set_language( buf, hb_language_from_string(languages[i], strlen(languages[i])) ); /* Layout the text */ hb_buffer_add_utf8( buf, texts[i], strlen(texts[i]), 0, strlen(texts[i]) ); hb_shape( hb_ft_font[i], buf, NULL, 0 ); unsigned int glyph_count; hb_glyph_info_t *glyph_info = hb_buffer_get_glyph_infos(buf, &glyph_count); hb_glyph_position_t *glyph_pos = hb_buffer_get_glyph_positions(buf, &glyph_count); FT_GlyphSlot slot; FT_Bitmap ft_bitmap; float size = 24; size_t hres = 64; FT_Error error; FT_Int32 flags = 0; flags |= FT_LOAD_RENDER; flags |= FT_LOAD_TARGET_LCD; FT_Library_SetLcdFilter( ft_library, FT_LCD_FILTER_LIGHT ); FT_Matrix matrix = { (int)((1.0/hres) * 0x10000L), (int)((0.0) * 0x10000L), (int)((0.0) * 0x10000L), (int)((1.0) * 0x10000L) }; /* Set char size */ error = FT_Set_Char_Size( ft_face[i], (int)(ptSize), 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( ft_face[i] ); break; } /* Set transform matrix */ FT_Set_Transform( ft_face[i], &matrix, NULL ); for (j = 0; j < glyph_count; ++j) { /* Load glyph */ error = FT_Load_Glyph( ft_face[i], glyph_info[j].codepoint, 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( ft_face[i] ); break; } slot = ft_face[i]->glyph; ft_bitmap = slot->bitmap; int ft_bitmap_width = slot->bitmap.width; int ft_bitmap_rows = slot->bitmap.rows; int ft_bitmap_pitch = slot->bitmap.pitch; int ft_glyph_top = slot->bitmap_top; int ft_glyph_left = slot->bitmap_left; int w = ft_bitmap_width/3; // 3 because of LCD/RGB encoding int h = ft_bitmap_rows; ivec4 region = texture_atlas_get_region( atlas, w+1, h+1 ); if ( region.x < 0 ) { fprintf( stderr, "Texture atlas is full (line %d)\n", __LINE__ ); continue; } int x = region.x, y = region.y; texture_atlas_set_region( atlas, region.x, region.y, w, h, ft_bitmap.buffer, ft_bitmap.pitch ); printf("%d: %dx%d %f %f\n", glyph_info[j].codepoint, ft_bitmap_width, ft_bitmap_rows, glyph_pos[j].x_advance/64., glyph_pos[j].y_advance/64.); } /* clean up the buffer, but don't kill it just yet */ hb_buffer_reset(buf); } /* Cleanup */ hb_buffer_destroy( buf ); for( i=0; i < NUM_EXAMPLES; ++i ) hb_font_destroy( hb_ft_font[i] ); FT_Done_FreeType( ft_library ); glClearColor(1,1,1,1); glEnable( GL_BLEND ); glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA ); glEnable( GL_TEXTURE_2D ); glBindTexture( GL_TEXTURE_2D, atlas->id ); texture_atlas_upload( atlas ); typedef struct { float x,y,z, u,v, r,g,b,a, shift, gamma; } vertex_t; vertex_t vertices[4] = { { 0, 0,0, 0,1, 0,0,0,1, 0, 1}, { 0,512,0, 0,0, 0,0,0,1, 0, 1}, {512,512,0, 1,0, 0,0,0,1, 0, 1}, {512, 0,0, 1,1, 0,0,0,1, 0, 1} }; GLuint indices[6] = { 0, 1, 2, 0,2,3 }; buffer = vertex_buffer_new( "vertex:3f," "tex_coord:2f," "color:4f," "ashift:1f," "agamma:1f" ); vertex_buffer_push_back( buffer, vertices, 4, indices, 6 ); shader = shader_load("shaders/text.vert", "shaders/text.frag"); mat4_set_identity( &projection ); mat4_set_identity( &model ); mat4_set_identity( &view ); glutMainLoop( ); return 0; }
// ----------------------------------------------- texture_font_load_glyphs --- size_t texture_font_load_glyphs( texture_font_t * self, const wchar_t * charcodes ) { size_t i, j, 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; FT_Int32 flags = 0; int ft_glyph_top = 0; int ft_glyph_left = 0; ivec4 region; size_t missed = 0; char pass; assert( self ); assert( charcodes ); width = self->atlas->width; height = self->atlas->height; depth = self->atlas->depth; if (!texture_font_get_face(self, &library, &face)) return wcslen(charcodes); /* Load each glyph */ for( i = 0; i < wcslen(charcodes); ++i ) { pass = 0; /* Check if charcode has been already loaded */ for( j = 0; j < self->glyphs->size; ++j ) { glyph = *(texture_glyph_t **) vector_get( self->glyphs, j ); // If charcode is -1, we don't care about outline type or thickness // if( (glyph->charcode == charcodes[i])) { if( (glyph->charcode == charcodes[i]) && ((charcodes[i] == (wchar_t)(-1) ) || ((glyph->outline_type == self->outline_type) && (glyph->outline_thickness == self->outline_thickness)) )) { pass = 1; break; } } if(pass) continue; flags = 0; ft_glyph_top = 0; 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 ) { FT_Done_Face( face ); FT_Done_FreeType( library ); throw wcslen(charcodes)-i; } if( self->outline_type == 0 ) { slot = face->glyph; ft_bitmap = slot->bitmap; 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 ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } FT_Stroker_Set(stroker, (int)(self->outline_thickness * HRES), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0); error = FT_Get_Glyph( face->glyph, &ft_glyph); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } 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 ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } if( depth == 1) { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_NORMAL, 0, 1); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } } else { error = FT_Glyph_To_Bitmap( &ft_glyph, FT_RENDER_MODE_LCD, 0, 1); if( error ) { FT_Done_Face( face ); FT_Stroker_Done( stroker ); FT_Done_FreeType( library ); throw; } } ft_bitmap_glyph = (FT_BitmapGlyph) ft_glyph; ft_bitmap = ft_bitmap_glyph->bitmap; 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 / HRESf; glyph->advance_y = slot->advance.y / HRESf; 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; }