Está na hora de descobrir o freetype2. Agora, quero disponibilizar meu código para aqueles que precisam. Porque como pensar em como trabalhar com a biblioteca, nem sempre há tempo. Quero mostrar o código de trabalho com freetype e um pouco com opengl. Um pouco sobre o código. Não consigo criar código complexo. Tudo acaba de alguma forma simples para mim. Vi vários trechos de código trabalhando com o freetype2 e não conseguia entender como ele realmente funciona. Já um código muito complexo foi criado pelos autores. Espero que você goste do meu código simples. Depois de ler este artigo, você pode criar texto com várias linhas e exibi-lo como uma textura única na tela.
Então, vamos começar.
A primeira coisa que gostaria de escrever aqui é o shader que escrevi para fora do livro. Impõe uma textura bidimensional em vários triângulos.
Para criar shaders, tenho uma classe separada. Escrevo para ele qual shader compilar e ele devolve o programa para mim. Ele também adiciona um programa pelo nome ao contêiner std :: map, para que eu possa obter o programa desse shader específico em outra seção do código.
GLuint ShaderManager::createProgram ( const char *param ) { if ( !strncmp ( param, "sprite\0",7 ) ) { const char *vshader = "#version 300 es\n" "layout(location = 0) in vec2 position;\n" "layout(location = 1) in vec2 texCoord;\n" "uniform mat4 transform;\n" "out vec2 v_texCoord;\n" "void main ( )\n" "{\n" " gl_Position = transform * vec4 ( position, 0.0, 1.0 );\n" " v_texCoord = texCoord;\n" "}"; const char *fshader = "#version 300 es\n" "precision mediump float;\n" "in vec2 v_texCoord;\n" "layout(location = 0) out vec4 outColor;\n" "uniform sampler2D s_texture;\n" "void main ( )\n" "{\n" " outColor = texture ( s_texture, v_texCoord );\n" "}"; GLuint program = loadProgram ( vshader, fshader ); global.programs["sprite"] = program; return program;
Em seguida, criei uma classe de fonte. Um objeto desta classe inicializa o texto, indica uma posição na tela e desenha uma textura.
#ifndef H_FONT_H #define H_FONT_H #include <stdint.h> #include <ft2build.h> #include <string> #include <vector> #include <SDL2/SDL_opengl.h> #include <SDL2/SDL_opengles2.h> #include "gl_mat.hpp" #include "global.hpp" #include <wchar.h> #include FT_FREETYPE_H #include FT_GLYPH_H class Font { public: Font ( ) { } /* freetype ttf . */ Font ( const char *ttf_file ); /* */ void setPos ( int x, int y ); /* . *\1 . *\2 . *\3 . *\4 . *\5 . *\6 . *\7 . *\8 . * */ void init ( wchar_t *text, int fontSize, int align, int valign, int space, uint8_t r, uint8_t g, uint8_t b ); /* */ void setSize ( int w, int h ); /* */ void draw ( ); private: FT_Face face = 0; /* */ float *texture; /* */ float *vertices; /* : */ int width; /* : */ int height; /* */ int sampler; /* id */ GLuint textureid; /* x */ int x; /* y */ int y; /* glOrtho */ float ortho[4][4]; /* */ float translate[4][4]; /* */ float result[4][4]; /* */ unsigned int program; FT_Library ft_library; FT_Face ttf; }; #endif
Bem, a aula está pronta. Agora vamos começar. Estou fazendo um jogo no android com sdl2 e testando no pc. Portanto, conheço uma maneira única de exibir dados na tela usando gles2 e opengl.
Então, vamos começar.
#include "font.hpp" Font::Font ( const char *ttf_file ) { glm::clearMatrix4x4 ( &ortho[0] ); glm::clearMatrix4x4 ( &translate[0] ); glm::clearMatrix4x4 ( &result[0] ); program = global.programs["sprite"]; int width = global.width; int height = global.height; glm::ortho ( &ortho[0], 0.0f, width, 0.0f, height, 0.0f, 1.0f ); setPos ( 0, 0 ); FT_Init_FreeType( &ft_library ); #ifdef __ANDROID__ FT_NewFace ( ft_library, ttf_file, 0, &face ); #else char *path = (char *) new char[255]; sprintf ( path, "assets/%s", ttf_file ); FT_New_Face ( ft_library, path, 0, &face ); delete[] path; #endif } void Font::init ( wchar_t *es, int fontSize, int align, int vert, int space, uint8_t r, uint8_t g, uint8_t b ) { FT_Set_Pixel_Sizes ( face, 0, fontSize ); FT_Glyph glyph; int w = 0; unsigned int h = 0; unsigned int maxh = 0; unsigned int toprow = 0; int len = wcslen ( es ); for ( int i = 0; i < len; i++ ) { /* */ wchar_t charcode = es[i]; /* bitmap */ FT_Load_Char ( face, charcode, FT_LOAD_RENDER ); FT_UInt glyph_index = FT_Get_Char_Index ( face, charcode ) FT_Load_Glyph ( face, glyph_index, FT_LOAD_DEFAULT ); FT_Render_Glyph ( face->glyph, FT_RENDER_MODE_NORMAL ); FT_Get_Glyph ( face->glyph, &glyph ); FT_Glyph_To_Bitmap ( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph; FT_Bitmap bitmap = bitmap_glyph->bitmap; /* */ w += bitmap.width; /* . */ int resize = bitmap.rows > bitmap_glyph->top ? bitmap.rows - bitmap_glyph->top : bitmap_glyph->top - bitmap.rows; /* */ if ( h < bitmap.rows + resize ) h = bitmap.rows + resize; /* */ if ( toprow < bitmap.rows ) toprow = bitmap.rows; if ( maxh < bitmap.rows + bitmap_glyph->top ) maxh = bitmap.rows + bitmap_glyph->top; /* , w , * */ if ( charcode == ' ' ) w += space; /* ' ' * */ if ( charcode == '\n' ) { h += vert + maxh; FT_Done_Glyph ( glyph ); continue; } /* , align , */ w += align; FT_Done_Glyph ( glyph ); } /* , * */ if ( h <= 0 ) h = maxh; uint8_t im[h][w]; /* */ memset ( &im[0][0], 0, w * h * sizeof ( uint8_t ) ); int ih = 0; int iw = 0; int posy = 0; int topy = 0; int maxwidth = 0; for ( int i = 0; i < len; i++ ) { wchar_t charcode = es[i]; FT_Load_Char ( face, charcode, FT_LOAD_RENDER ); FT_UInt glyph_index = FT_Get_Char_Index ( face, charcode ); FT_Load_Glyph ( face, glyph_index, FT_LOAD_DEFAULT ); FT_Render_Glyph ( face->glyph, FT_RENDER_MODE_NORMAL ); FT_Get_Glyph ( face->glyph, &glyph ); FT_Glyph_To_Bitmap ( &glyph, FT_RENDER_MODE_NORMAL, 0, 1 ); FT_BitmapGlyph bitmap_glyph = (FT_BitmapGlyph) glyph; FT_Bitmap bitmap = bitmap_glyph->bitmap; /* */ posy = bitmap_glyph->top; /* , */ posy = bitmap.rows - posy; topy = toprow - bitmap.rows; /* , ih - , , * */ if ( charcode == '\n' ) { ih += maxh; iw = 0; FT_Done_Glyph ( glyph ); continue; } for ( unsigned int y = 0, i = 0; y < bitmap.rows; y++ ) { for ( unsigned int x = 0; x < bitmap.width; x++, i++ ) { if ( ( ih + posy + y + topy ) > h ) { if ( posy < 0 ) posy = abs ( posy ); } /* * gray, */ im [ ih + posy + y + topy ] [ iw + x ] = bitmap.buffer[i]; } } /* */ iw += bitmap.width; /* */ iw += align; if ( maxwidth < iw ) maxwidth = iw; if ( charcode == ' ' ) { iw += space; } FT_Done_Glyph ( glyph ); } iw = maxwidth; width = iw; height = h; unsigned int size = width * height; /* */ uint8_t *image_data = new uint8_t [ size * 4 ]; /* */ memset ( image_data, 255, size * 4 * sizeof ( uint8_t ) ); for ( unsigned int i = 0, y = 0; i < size; y++ ) { for ( int x = 0; x < width; x++, i++ ) { /* */ image_data[ 4 * i + 3] = im [ y ][ x ]; /* */ image_data[ 4 * i + 0] = r; image_data[ 4 * i + 1] = g; image_data[ 4 * i + 2] = b; } } /* */ glGenTextures ( 1, &textureid ); glBindTexture ( GL_TEXTURE_2D, textureid ); glTexImage2D ( GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, image_data ); 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_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); /* */ setSize ( width, height ); /* , image_data . */ delete[] image_data; } void Font::setSize ( int w, int h ) { /* , , */ if ( vertices ) delete[] vertices; if ( texture ) delete[] texture; vertices = new float [ 12 ]; vertices[0] = 0; vertices[1] = 0; vertices[2] = 0; vertices[3] = h; vertices[4] = w; vertices[5] = 0; vertices[6] = w; vertices[7] = 0; vertices[8] = w; vertices[9] = h; vertices[10] = 0; vertices[11] = h; /* , * */ texture = new float [ 12 ]; texture[0] = 0; texture[1] = 1; texture[2] = 0; texture[3] = 0; texture[4] = 1; texture[5] = 1; texture[6] = 1; texture[7] = 1; texture[8] = 1; texture[9] = 0; texture[10] = 0; texture[11] = 0; } void Font::setPos ( int x, int y ) { /* , */ this->x = x; this->y = y; glm::translate ( &translate[0], x, y, 0 ); glm::sumMatrix ( &result[0], &translate[0], &ortho[0] ); } void Font::draw ( ) { /* */ glUseProgram ( program ); sampler = glGetUniformLocation ( program, "s_texture" ); glActiveTexture ( GL_TEXTURE0 ); glBindTexture ( GL_TEXTURE_2D, textureid ); glUniform1i ( sampler, 0 ); GLint projection_location = glGetUniformLocation ( program, "transform" ); glUniformMatrix4fv ( projection_location, 1, GL_FALSE, &result[0][0] ); glEnableVertexAttribArray ( 0 ); glEnableVertexAttribArray ( 1 ); /* */ glVertexAttribPointer ( 0, 2, GL_FLOAT, GL_FALSE, 0, vertices ); /* */ glVertexAttribPointer ( 1, 2, GL_FLOAT, GL_FALSE, 0, texture ); /* */ glDrawArrays ( GL_TRIANGLES, 0, 12 ); glDisableVertexAttribArray ( 0 ); glDisableVertexAttribArray ( 1 ); }
No código, você pode chamar essa função assim.
Font *font = new Font ("anonymous.ttf"); wchar_t * text = L" habr. . freetype opengl.\n" " freetype .\n" " , "; font->init ( text, 21, 1, 4, 4, 0, 0, 0 ); font->setPos ( 100, 100 );
