Es ist Zeit, dass ich freetype2 herausfinde. Jetzt möchte ich meinen Code den Bedürftigen zur Verfügung stellen. Denn wie man über die Arbeit mit der Bibliothek nachdenkt, hat nicht immer Zeit. Ich möchte den Arbeitscode mit Freetype und ein bisschen mit opengl anzeigen. Ein bisschen über den Code. Ich kann keinen komplexen Code erstellen. Alles fällt mir irgendwie einfach. Ich habe mehrere Codeteile gesehen, die mit freetype2 arbeiten, und konnte nicht verstehen, wie es tatsächlich funktioniert. Bereits ein sehr komplexer Code wurde von den Autoren erstellt. Ich hoffe dir gefällt mein einfacher Code. Nach dem Lesen dieses Artikels können Sie mehrzeiligen Text erstellen und als einzelne Textur auf dem Bildschirm anzeigen.
Also fangen wir an.
Das erste, was ich hier schreiben möchte, ist der Shader, den ich aus dem Buch geschrieben habe. Es legt mehreren Dreiecken eine zweidimensionale Textur auf.
Zum Erstellen von Shadern habe ich eine separate Klasse. Ich schreibe ihm, welchen Shader er kompilieren soll, und er gibt mir das Programm zurück. Er fügt dem std :: map-Container auch ein Programm mit Namen hinzu, damit ich das Programm dieses bestimmten Shaders in einem anderen Abschnitt des Codes abrufen kann.
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;
Als Nächstes habe ich eine Schriftklasse erstellt. Ein Objekt dieser Klasse initialisiert Text, gibt eine Position auf dem Bildschirm an und zeichnet eine Textur.
#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
Nun, die Klasse ist fertig. Jetzt fangen wir an. Ich mache ein Spiel auf Android mit SDL2 und teste auf dem PC. Daher kenne ich eine einzige Möglichkeit, Daten mit gles2 und opengl auf dem Bildschirm anzuzeigen.
Also fangen wir an.
#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 ); }
Über den Code können Sie diese Funktion folgendermaßen aufrufen.
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 );
