1 /*
2  * File:   gl_font.c
3  * Author: TeslaRus
4  *
5  * Created on January 16, 2015, 10:46 PM
6  */
7 
8 #include <stdint.h>
9 #include <SDL2/SDL_platform.h>
10 #include <SDL2/SDL_opengl.h>
11 #include <math.h>
12 
13 #include <ft2build.h>
14 #include <freetype.h>
15 #include <ftglyph.h>
16 #include <ftmodapi.h>
17 
18 #include "utf8_32.h"
19 #include "gl_font.h"
20 #include "gl_util.h"
21 
22 //T4Larson <t4larson@gmail.com>: fixed font construction and destruction!
23 #define vec4_copy(x, y) {(x)[0] = (y)[0]; (x)[1] = (y)[1]; (x)[2] = (y)[2]; (x)[3] = (y)[3];}
24 
25 static FT_Library g_ft_library = NULL;
26 
27 typedef struct char_info_s
28 {
29     GLuint          tex_index;
30     GLint           width;
31     GLint           height;
32     GLint           left;
33     GLint           top;
34 
35     GLfloat         tex_x0;
36     GLfloat         tex_y0;
37     GLfloat         tex_x1;
38     GLfloat         tex_y1;
39 
40     GLfloat         advance_x_pt;
41     GLfloat         advance_y_pt;
42 }char_info_t, *char_info_p;
43 
glf_init()44 void glf_init()
45 {
46     if(!g_ft_library)
47     {
48         FT_Init_FreeType(&g_ft_library);
49     }
50 }
51 
glf_destroy()52 void glf_destroy()
53 {
54     if(g_ft_library)
55     {
56         FT_Done_FreeType(g_ft_library);
57         g_ft_library = NULL;
58     }
59 }
60 
glf_create_font(const char * file_name,uint16_t font_size)61 gl_tex_font_p glf_create_font(const char *file_name, uint16_t font_size)
62 {
63     if(g_ft_library)
64     {
65         gl_tex_font_p glf = (gl_tex_font_p)malloc(sizeof(gl_tex_font_t));
66         glf->ft_face = NULL;
67 
68         if(FT_New_Face(g_ft_library, file_name, 0, (FT_Face*)&glf->ft_face))
69         {
70             free(glf);
71             return NULL;
72         }
73 
74         glf->glyphs_count = ((FT_Face)glf->ft_face)->num_glyphs;
75         glf->glyphs = (char_info_p)malloc(glf->glyphs_count * sizeof(char_info_t));
76 
77         qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &glf->gl_max_tex_width);
78         glf->gl_tex_width = glf->gl_max_tex_width;
79         glf->gl_tex_indexes = NULL;
80         glf->gl_tex_indexes_count = 0;
81         glf->gl_real_tex_indexes_count = 0;
82         glf->gl_font_color[0] = 0.0;
83         glf->gl_font_color[1] = 0.0;
84         glf->gl_font_color[2] = 0.0;
85         glf->gl_font_color[3] = 1.0;
86 
87         glf_resize(glf, font_size);
88         FT_Select_Charmap(glf->ft_face, FT_ENCODING_UNICODE);
89 
90         return glf;
91     }
92 
93     return NULL;
94 }
95 
96 /**
97  * Creates gl texture font from true type font;
98  * @param ft_library: base font library;
99  * @param face_data: pointer to the buffer with font file content; DO NOT FREE that pointer otherway using FT_Face prevets to crash;
100  * @param face_data_size: size of buffer with font file content;
101  * @param font_size: size of font glyph?
102  * @return pointer to the gl_tex_font_s structure;
103  */
glf_create_font_mem(void * face_data,size_t face_data_size,uint16_t font_size)104 gl_tex_font_p glf_create_font_mem(void *face_data, size_t face_data_size, uint16_t font_size)
105 {
106     if(g_ft_library)
107     {
108         gl_tex_font_p glf = (gl_tex_font_p)malloc(sizeof(gl_tex_font_t));
109         glf->ft_face = NULL;
110 
111         if(FT_New_Memory_Face(g_ft_library, (const FT_Byte*)face_data, face_data_size, 0, (FT_Face*)&glf->ft_face))
112         {
113             free(glf);
114             return NULL;
115         }
116 
117         glf->glyphs_count = ((FT_Face)glf->ft_face)->num_glyphs;
118         glf->glyphs = (char_info_p)malloc(glf->glyphs_count * sizeof(char_info_t));
119 
120         qglGetIntegerv(GL_MAX_TEXTURE_SIZE, &glf->gl_max_tex_width);
121         glf->gl_tex_width = glf->gl_max_tex_width;
122         glf->gl_tex_indexes = NULL;
123         glf->gl_tex_indexes_count = 0;
124         glf->gl_real_tex_indexes_count = 0;
125         glf_resize(glf, font_size);
126         FT_Select_Charmap(glf->ft_face, FT_ENCODING_UNICODE);
127 
128         return glf;
129     }
130 
131     return NULL;
132 }
133 
134 
glf_free_font(gl_tex_font_p glf)135 void glf_free_font(gl_tex_font_p glf)
136 {
137     if(glf != NULL)
138     {
139         if(glf->ft_face != NULL)
140         {
141             FT_Done_Face(glf->ft_face);
142         }
143 
144         glf->ft_face = NULL;
145         if(glf->glyphs != NULL)
146         {
147             free(glf->glyphs);
148             glf->glyphs = NULL;
149         }
150         glf->glyphs_count = 0;
151 
152         glf->gl_real_tex_indexes_count = 0;
153         if(glf->gl_tex_indexes != NULL)
154         {
155             if(glf->gl_tex_indexes_count > 0)
156             {
157                 qglDeleteTextures(glf->gl_tex_indexes_count, glf->gl_tex_indexes);
158             }
159             free(glf->gl_tex_indexes);
160         }
161         glf->gl_tex_indexes_count = 0;
162         glf->gl_tex_indexes = NULL;
163 
164         free(glf);
165     }
166 }
167 
168 
NextPowerOf2(GLuint in)169 static __inline GLuint NextPowerOf2(GLuint in)
170 {
171      in -= 1;
172 
173      in |= in >> 16;
174      in |= in >> 8;
175      in |= in >> 4;
176      in |= in >> 2;
177      in |= in >> 1;
178 
179      return in + 1;
180 }
181 
182 
bbox_add(int32_t * x0,int32_t * x1,int32_t * y0,int32_t * y1,int32_t * x_min,int32_t * x_max,int32_t * y_min,int32_t * y_max)183 static __inline void bbox_add(int32_t *x0, int32_t *x1, int32_t *y0, int32_t *y1,
184                               int32_t *x_min, int32_t *x_max, int32_t *y_min, int32_t *y_max)
185 {
186     int32_t min, max;
187 
188     if(*x0 > *x1)
189     {
190         min = *x1;
191         max = *x0;
192     }
193     else
194     {
195         min = *x0;
196         max = *x1;
197     }
198 
199     if(*x_min > min)
200     {
201         *x_min = min;
202     }
203     if(*x_max < max)
204     {
205         *x_max = max;
206     }
207 
208     if(*y0 > *y1)
209     {
210         min = *y1;
211         max = *y0;
212     }
213     else
214     {
215         min = *y0;
216         max = *y1;
217     }
218 
219     if(*y_min > min)
220     {
221         *y_min = min;
222     }
223     if(*y_max < max)
224     {
225         *y_max = max;
226     }
227 }
228 
229 
glf_resize(gl_tex_font_p glf,uint16_t font_size)230 void glf_resize(gl_tex_font_p glf, uint16_t font_size)
231 {
232     if((glf != NULL) && (glf->ft_face != NULL))
233     {
234         const GLint padding = 2;
235         GLubyte *buffer;
236         GLint chars_in_row, chars_in_column;
237         size_t buffer_size;
238         int x, y, xx, yy;
239         int i, ii, i0 = 0;
240 
241         // clear old atlas, if exists
242         if(glf->gl_tex_indexes != NULL)
243         {
244             if(glf->gl_tex_indexes_count > 0)
245             {
246                 qglDeleteTextures(glf->gl_tex_indexes_count, glf->gl_tex_indexes);
247             }
248             free(glf->gl_tex_indexes);
249         }
250         glf->gl_tex_indexes = NULL;
251         glf->gl_real_tex_indexes_count = 0;
252 
253         // resize base font
254         glf->font_size = font_size;
255         FT_Set_Char_Size(glf->ft_face, font_size << 6, font_size << 6, 0, 0);
256 
257         // calculate texture atlas size
258         chars_in_row = 1 + sqrt(glf->glyphs_count);
259         glf->gl_tex_width = (font_size + padding) * chars_in_row;
260         glf->gl_tex_width = NextPowerOf2(glf->gl_tex_width);
261         if(glf->gl_tex_width > glf->gl_max_tex_width)
262         {
263             glf->gl_tex_width = glf->gl_max_tex_width;
264         }
265 
266         // create new atlas
267         chars_in_row = glf->gl_tex_width / (font_size + padding);
268         chars_in_column = glf->glyphs_count / chars_in_row + 1;
269         glf->gl_tex_indexes_count = (chars_in_column * (font_size + padding)) / glf->gl_tex_width + 1;
270         glf->gl_tex_indexes = (GLuint*)malloc(glf->gl_tex_indexes_count * sizeof(GLuint));
271         qglGenTextures(glf->gl_tex_indexes_count, glf->gl_tex_indexes);
272 
273         buffer_size = glf->gl_tex_width * glf->gl_tex_width * sizeof(GLubyte);
274         buffer = (GLubyte*)malloc(buffer_size);
275         memset(buffer, 0x00, buffer_size);
276 
277         for(i = 0, x = 0, y = 0; i < glf->glyphs_count; i++)
278         {
279             FT_GlyphSlot g;
280             glf->glyphs[i].tex_index = 0;
281 
282             /* load glyph image into the slot (erase previous one) */
283             if(FT_Load_Glyph(glf->ft_face, i, FT_LOAD_RENDER))
284             {
285                 continue;
286             }
287             /* convert to an anti-aliased bitmap */
288             if(FT_Render_Glyph(((FT_Face)glf->ft_face)->glyph, FT_RENDER_MODE_NORMAL))
289             {
290                 continue;
291             }
292 
293             g = ((FT_Face)glf->ft_face)->glyph;
294             glf->glyphs[i].width = g->bitmap.width;
295             glf->glyphs[i].height = g->bitmap.rows;
296             glf->glyphs[i].advance_x_pt = g->advance.x;
297             glf->glyphs[i].advance_y_pt = g->advance.y;
298             glf->glyphs[i].left = g->bitmap_left;
299             glf->glyphs[i].top = g->bitmap_top;
300 
301             if((g->bitmap.width == 0) || (g->bitmap.rows == 0))
302             {
303                 continue;
304             }
305 
306             if(x + g->bitmap.width > glf->gl_tex_width)
307             {
308                 x = 0;
309                 y += glf->font_size + padding;
310                 if(y + glf->font_size > glf->gl_tex_width)
311                 {
312                     int ii;
313                     qglBindTexture(GL_TEXTURE_2D, glf->gl_tex_indexes[glf->gl_real_tex_indexes_count]);
314                     qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
315                     qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
316                     qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
317                     qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
318                     qglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glf->gl_tex_width, glf->gl_tex_width, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
319                     for(ii = i0; ii < i; ii++)
320                     {
321                         glf->glyphs[ii].tex_x0 /= (GLfloat)glf->gl_tex_width;
322                         glf->glyphs[ii].tex_x1 /= (GLfloat)glf->gl_tex_width;
323                         glf->glyphs[ii].tex_y0 /= (GLfloat)glf->gl_tex_width;
324                         glf->glyphs[ii].tex_y1 /= (GLfloat)glf->gl_tex_width;
325                     }
326                     memset(buffer, 0x00, buffer_size);
327                     y = 0;
328                     i0 = i;
329                     glf->gl_real_tex_indexes_count++;
330                 }
331             }
332 
333             glf->glyphs[i].tex_x0 = (GLfloat)x;
334             glf->glyphs[i].tex_y0 = (GLfloat)y;
335             glf->glyphs[i].tex_x1 = (GLfloat)(x + g->bitmap.width);
336             glf->glyphs[i].tex_y1 = (GLfloat)(y + g->bitmap.rows);
337 
338             glf->glyphs[i].tex_index = glf->gl_tex_indexes[glf->gl_real_tex_indexes_count];
339             for(xx = 0; xx < g->bitmap.width; xx++)
340             {
341                 for(yy = 0; yy < g->bitmap.rows; yy++)
342                 {
343                     buffer[(y+yy)*glf->gl_tex_width + (x+xx)] = g->bitmap.buffer[yy * g->bitmap.width + xx];
344                 }
345             }
346 
347             x += (g->bitmap.width + padding);
348         }
349 
350         qglBindTexture(GL_TEXTURE_2D, glf->gl_tex_indexes[glf->gl_real_tex_indexes_count]);
351         qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
352         qglTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
353         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
354         qglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
355         chars_in_column = NextPowerOf2(y + font_size + padding);
356         qglTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, glf->gl_tex_width, chars_in_column, 0, GL_ALPHA, GL_UNSIGNED_BYTE, buffer);
357         for(ii = i0; ii < glf->glyphs_count; ii++)
358         {
359             glf->glyphs[ii].tex_x0 /= (GLfloat)glf->gl_tex_width;
360             glf->glyphs[ii].tex_x1 /= (GLfloat)glf->gl_tex_width;
361             glf->glyphs[ii].tex_y0 /= (GLfloat)chars_in_column;
362             glf->glyphs[ii].tex_y1 /= (GLfloat)chars_in_column;
363         }
364         free(buffer);
365         glf->gl_real_tex_indexes_count++;
366     }
367 }
368 
369 
glf_get_ascender(gl_tex_font_p glf)370 int32_t glf_get_ascender(gl_tex_font_p glf)
371 {
372     if((glf->font_size == 0) || (glf->ft_face == NULL))
373     {
374         return 0.0;
375     }
376 
377     return ((FT_Face)glf->ft_face)->ascender;
378 }
379 
380 
glf_get_font_size(gl_tex_font_p glf)381 uint16_t glf_get_font_size(gl_tex_font_p glf)
382 {
383     if((glf != NULL) && (glf->ft_face != NULL))
384     {
385         return glf->font_size;
386     }
387     else
388     {
389         return 0;
390     }
391 }
392 
393 
glf_get_string_len(gl_tex_font_p glf,const char * text,int n)394 int32_t glf_get_string_len(gl_tex_font_p glf, const char *text, int n)
395 {
396     int32_t x = 0;
397     uint8_t *ch = (uint8_t*)text;
398 
399     if(glf && glf->ft_face && *ch)
400     {
401         uint32_t curr_utf32, next_utf32;
402         int i = 0;
403         FT_Vector kern;
404 
405         ch = utf8_to_utf32(ch, &curr_utf32);
406         curr_utf32 = FT_Get_Char_Index(glf->ft_face, curr_utf32);
407         for(; (n < 0) || (i < n); i++)
408         {
409             n = (*ch) ? (n) : (0);
410             ch = utf8_to_utf32(ch, &next_utf32);
411             next_utf32 = FT_Get_Char_Index(glf->ft_face, next_utf32);
412 
413             FT_Get_Kerning(glf->ft_face, curr_utf32, next_utf32, FT_KERNING_UNSCALED, &kern);   // kern in 1/64 pixel
414             curr_utf32 = next_utf32;
415             x += kern.x + glf->glyphs[curr_utf32].advance_x_pt;
416         }
417     }
418 
419     return x;
420 }
421 
422 
glf_get_string_for_width(gl_tex_font_p glf,char * text,int32_t w_pt,int * n_sym)423 char *glf_get_string_for_width(gl_tex_font_p glf, char *text, int32_t w_pt, int *n_sym)
424 {
425     int32_t x = 0;
426     uint8_t *ch = (uint8_t*)text;
427     char *ret = text;
428     *n_sym = 0;
429 
430     if(glf && glf->ft_face && *ch)
431     {
432         uint32_t curr_utf32, next_utf32;
433         FT_Vector kern;
434 
435         ch = utf8_to_utf32(ch, &curr_utf32);
436         curr_utf32 = FT_Get_Char_Index(glf->ft_face, curr_utf32);
437         w_pt -= glf->glyphs[curr_utf32].advance_x_pt;
438         do
439         {
440             ret = (char*)ch;
441             (*n_sym)++;
442             w_pt = (*ch) ? (w_pt) : (0);
443             ch = utf8_to_utf32(ch, &next_utf32);
444             next_utf32 = FT_Get_Char_Index(glf->ft_face, next_utf32);
445 
446             FT_Get_Kerning(glf->ft_face, curr_utf32, next_utf32, FT_KERNING_UNSCALED, &kern);   // kern in 1/64 pixel
447             curr_utf32 = next_utf32;
448             x += kern.x + glf->glyphs[curr_utf32].advance_x_pt;
449         }
450         while(x < w_pt);
451     }
452 
453     return ret;
454 }
455 
456 
glf_get_string_bb(gl_tex_font_p glf,const char * text,int n,int32_t * x0,int32_t * y0,int32_t * x1,int32_t * y1)457 void glf_get_string_bb(gl_tex_font_p glf, const char *text, int n, int32_t *x0, int32_t *y0, int32_t *x1, int32_t *y1)
458 {
459     uint8_t *ch = (uint8_t*)text;
460     *x0 = 0;
461     *x1 = 0;
462     *y0 = 0;
463     *y1 = 0;
464 
465     if(glf && glf->ft_face && *ch)
466     {
467         FT_Vector kern;
468         int32_t x_pt = 0;
469         int32_t y_pt = 0;
470         int32_t xx0, xx1, yy0, yy1;
471         uint32_t curr_utf32, next_utf32;
472 
473         ch = utf8_to_utf32(ch, &curr_utf32);
474         curr_utf32 = FT_Get_Char_Index(glf->ft_face, curr_utf32);
475         for(int i = 0; (n < 0) || (i < n); i++)
476         {
477             char_info_p g = glf->glyphs + curr_utf32;
478             n = (*ch) ? (n) : (0);
479 
480             ch = utf8_to_utf32(ch, &next_utf32);
481             next_utf32 = FT_Get_Char_Index(glf->ft_face, next_utf32);
482             FT_Get_Kerning(glf->ft_face, curr_utf32, next_utf32, FT_KERNING_UNSCALED, &kern);   // kern in 1/64 pixel
483             curr_utf32 = next_utf32;
484 
485             xx0 = x_pt + g->left * 64;
486             xx1 = xx0 + g->width * 64;
487             yy0 = y_pt + g->top * 64;
488             yy1 = yy0 - g->height * 64;
489             bbox_add(&xx0, &xx1, &yy0, &yy1, x0, x1, y0, y1);
490 
491             x_pt += kern.x + g->advance_x_pt;
492             y_pt += kern.y + g->advance_y_pt;
493         }
494     }
495 }
496 
497 
glf_render_str(gl_tex_font_p glf,GLfloat x,GLfloat y,const char * text,int32_t n_sym)498 void glf_render_str(gl_tex_font_p glf, GLfloat x, GLfloat y, const char *text, int32_t n_sym)
499 {
500     if(glf && glf->ft_face && text && (text[0] != 0))
501     {
502         uint8_t *nch, *ch = (uint8_t*)text;
503         FT_Vector kern;
504         int32_t x_pt = 0;
505         int32_t y_pt = 0;
506         if(glf->gl_real_tex_indexes_count == 1)
507         {
508             GLuint elements_count = 0;
509             uint32_t curr_utf32, next_utf32;
510             GLfloat *p, *buffer;
511 
512             buffer = (GLfloat*)malloc(48 * utf8_strlen(text) * sizeof(GLfloat));
513             nch = utf8_to_utf32(ch, &curr_utf32);
514             curr_utf32 = FT_Get_Char_Index(glf->ft_face, curr_utf32);
515 
516             qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
517             for(p = buffer; *ch && n_sym--;)
518             {
519                 char_info_p g;
520                 uint8_t *nch2 = utf8_to_utf32(nch, &next_utf32);
521 
522                 next_utf32 = FT_Get_Char_Index(glf->ft_face, next_utf32);
523                 ch = nch;
524                 nch = nch2;
525 
526                 g = glf->glyphs + curr_utf32;
527                 FT_Get_Kerning(glf->ft_face, curr_utf32, next_utf32, FT_KERNING_UNSCALED, &kern);   // kern in 1/64 pixel
528                 curr_utf32 = next_utf32;
529 
530                 if(g->tex_index != 0)
531                 {
532                     GLfloat x0 = x  + g->left + x_pt / 64.0f;
533                     GLfloat x1 = x0 + g->width;
534                     GLfloat y0 = y  + g->top + y_pt / 64.0f;
535                     GLfloat y1 = y0 - g->height;
536 
537                     *p = x0;            p++;
538                     *p = y0;            p++;
539                     *p = g->tex_x0;     p++;
540                     *p = g->tex_y0;     p++;
541                     vec4_copy(p, glf->gl_font_color);   p += 4;
542 
543                     *p = x1;            p++;
544                     *p = y0;            p++;
545                     *p = g->tex_x1;     p++;
546                     *p = g->tex_y0;     p++;
547                     vec4_copy(p, glf->gl_font_color);   p += 4;
548 
549                     *p = x1;            p++;
550                     *p = y1;            p++;
551                     *p = g->tex_x1;     p++;
552                     *p = g->tex_y1;     p++;
553                     vec4_copy(p, glf->gl_font_color);   p += 4;
554                     elements_count++;
555 
556                     *p = x0;            p++;
557                     *p = y0;            p++;
558                     *p = g->tex_x0;     p++;
559                     *p = g->tex_y0;     p++;
560                     vec4_copy(p, glf->gl_font_color);   p += 4;
561 
562                     *p = x1;            p++;
563                     *p = y1;            p++;
564                     *p = g->tex_x1;     p++;
565                     *p = g->tex_y1;     p++;
566                     vec4_copy(p, glf->gl_font_color);   p += 4;
567 
568                     *p = x0;            p++;
569                     *p = y1;            p++;
570                     *p = g->tex_x0;     p++;
571                     *p = g->tex_y1;     p++;
572                     vec4_copy(p, glf->gl_font_color);   p += 4;
573                     elements_count++;
574                 }
575                 x_pt += kern.x + g->advance_x_pt;
576                 y_pt += kern.y + g->advance_y_pt;
577             }
578             ///RENDER
579             if(elements_count != 0)
580             {
581                 qglBindTexture(GL_TEXTURE_2D, glf->gl_tex_indexes[0]);
582                 qglVertexPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), buffer+0);
583                 qglTexCoordPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), buffer+2);
584                 qglColorPointer(4, GL_FLOAT, 8 * sizeof(GLfloat), buffer+4);
585                 qglDrawArrays(GL_TRIANGLES, 0, elements_count * 3);
586             }
587             qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
588             free(buffer);
589         }
590         else
591         {
592             GLfloat *p, buffer[32];
593             GLuint active_texture = 0;
594             uint32_t curr_utf32, next_utf32;
595             nch = utf8_to_utf32(ch, &curr_utf32);
596             curr_utf32 = FT_Get_Char_Index(glf->ft_face, curr_utf32);
597             qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
598             for(; *ch && n_sym--;)
599             {
600                 char_info_p g;
601                 uint8_t *nch2 = utf8_to_utf32(nch, &next_utf32);
602 
603                 next_utf32 = FT_Get_Char_Index(glf->ft_face, next_utf32);
604                 ch = nch;
605                 nch = nch2;
606 
607                 g = glf->glyphs + curr_utf32;
608                 FT_Get_Kerning(glf->ft_face, curr_utf32, next_utf32, FT_KERNING_UNSCALED, &kern);   // kern in 1/64 pixel
609                 curr_utf32 = next_utf32;
610 
611                 if(g->tex_index != 0)
612                 {
613                     ///RENDER
614                     GLfloat x0 = x  + g->left + x_pt / 64.0f;
615                     GLfloat x1 = x0 + g->width;
616                     GLfloat y0 = y  + g->top + y_pt / 64.0f;
617                     GLfloat y1 = y0 - g->height;
618 
619                     p = buffer;
620                     *p = x0;            p++;
621                     *p = y0;            p++;
622                     *p = g->tex_x0;     p++;
623                     *p = g->tex_y0;     p++;
624                     vec4_copy(p, glf->gl_font_color);   p += 4;
625 
626                     *p = x1;            p++;
627                     *p = y0;            p++;
628                     *p = g->tex_x1;     p++;
629                     *p = g->tex_y0;     p++;
630                     vec4_copy(p, glf->gl_font_color);   p += 4;
631 
632                     *p = x1;            p++;
633                     *p = y1;            p++;
634                     *p = g->tex_x1;     p++;
635                     *p = g->tex_y1;     p++;
636                     vec4_copy(p, glf->gl_font_color);   p += 4;
637 
638                     *p = x0;            p++;
639                     *p = y1;            p++;
640                     *p = g->tex_x0;     p++;
641                     *p = g->tex_y1;     p++;
642                     vec4_copy(p, glf->gl_font_color);
643 
644                     if(active_texture != g->tex_index)
645                     {
646                         qglBindTexture(GL_TEXTURE_2D, g->tex_index);
647                         active_texture = g->tex_index;
648                     }
649                     qglVertexPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), buffer+0);
650                     qglTexCoordPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), buffer+2);
651                     qglColorPointer(4, GL_FLOAT, 8 * sizeof(GLfloat), buffer+4);
652                     qglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
653                 }
654                 x_pt += kern.x + g->advance_x_pt;
655                 y_pt += kern.y + g->advance_y_pt;
656             }
657         }
658     }
659 }
660