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