1 /*
2  * File:   gl_text.cpp
3  * Author: TeslaRus
4  *
5  * Created on October 24, 2015, 11:34 AM
6  */
7 
8 #include <SDL2/SDL_platform.h>
9 #include <SDL2/SDL_opengl.h>
10 #include <math.h>
11 #include <stdint.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 
15 #include "gl_text.h"
16 #include "gl_font.h"
17 #include "gl_util.h"
18 
19 
20 #define vec4_copy(x, y) {(x)[0] = (y)[0]; (x)[1] = (y)[1]; (x)[2] = (y)[2]; (x)[3] = (y)[3];}
21 
22 static struct
23 {
24     gl_text_line_p           gl_base_lines;
25     gl_text_line_t           gl_temp_lines[GLTEXT_MAX_TEMP_LINES];
26     uint16_t                 temp_lines_used;
27     uint16_t                 max_styles;
28     struct gl_fontstyle_s   *styles;
29 
30     uint16_t                 max_fonts;
31     struct gl_font_cont_s   *fonts;
32 } font_data;
33 
34 static int screen_width = 0;
35 static int screen_height = 0;
36 
37 
GLText_Init()38 void GLText_Init()
39 {
40     int i;
41 
42     font_data.max_styles = GLTEXT_MAX_FONTSTYLES;
43     font_data.styles     = (gl_fontstyle_p)malloc(font_data.max_styles * sizeof(gl_fontstyle_t));
44     for(i = 0; i < font_data.max_styles; i++)
45     {
46         font_data.styles[i].rect_color[0] = 1.0;
47         font_data.styles[i].rect_color[1] = 1.0;
48         font_data.styles[i].rect_color[2] = 1.0;
49         font_data.styles[i].rect_color[3] = 0.0;
50 
51         font_data.styles[i].font_color[0] = 0.0;
52         font_data.styles[i].font_color[1] = 0.0;
53         font_data.styles[i].font_color[2] = 0.0;
54         font_data.styles[i].font_color[3] = 1.0;
55 
56         font_data.styles[i].shadowed = 0x00;
57         font_data.styles[i].rect     = 0x00;
58     }
59 
60     font_data.max_fonts = GLTEXT_MAX_FONTS;
61     font_data.fonts     = (gl_font_cont_p)malloc(font_data.max_fonts * sizeof(gl_font_cont_t));
62     for(i = 0; i < font_data.max_fonts; i++)
63     {
64         font_data.fonts[i].font_size = 0;
65         font_data.fonts[i].gl_font   = NULL;
66     }
67 
68     for(int i = 0; i < GLTEXT_MAX_TEMP_LINES; i++)
69     {
70         font_data.gl_temp_lines[i].text_size = GUI_LINE_DEFAULTSIZE;
71         font_data.gl_temp_lines[i].text = (char*)malloc(GUI_LINE_DEFAULTSIZE * sizeof(char));
72         font_data.gl_temp_lines[i].text[0] = 0;
73         font_data.gl_temp_lines[i].show = 0;
74 
75         font_data.gl_temp_lines[i].next = NULL;
76         font_data.gl_temp_lines[i].prev = NULL;
77 
78         font_data.gl_temp_lines[i].font_id  = FONT_SECONDARY;
79         font_data.gl_temp_lines[i].style_id = FONTSTYLE_GENERIC;
80     }
81 
82     font_data.temp_lines_used = 0;
83 }
84 
85 
GLText_Destroy()86 void GLText_Destroy()
87 {
88     int i;
89 
90     for(i = 0; i < GLTEXT_MAX_TEMP_LINES ; i++)
91     {
92         font_data.gl_temp_lines[i].show = 0;
93         font_data.gl_temp_lines[i].text_size = 0;
94         free(font_data.gl_temp_lines[i].text);
95         font_data.gl_temp_lines[i].text = NULL;
96     }
97 
98     font_data.temp_lines_used = GLTEXT_MAX_TEMP_LINES;
99 
100     for(i = 0; i < font_data.max_fonts; i++)
101     {
102         glf_free_font(font_data.fonts[i].gl_font);
103         font_data.fonts[i].font_size = 0;
104         font_data.fonts[i].gl_font   = NULL;
105     }
106     free(font_data.fonts);
107     font_data.fonts = NULL;
108 
109     free(font_data.styles);
110     font_data.styles = NULL;
111 
112     font_data.max_fonts = 0;
113     font_data.max_styles = 0;
114 }
115 
116 
GLText_UpdateResize(int w,int h,float scale)117 void GLText_UpdateResize(int w, int h, float scale)
118 {
119     screen_width = w;
120     screen_height = h;
121     if(font_data.max_fonts > 0)
122     {
123         for(uint16_t i = 0; i < font_data.max_fonts; i++)
124         {
125             if(font_data.fonts[i].gl_font)
126             {
127                 glf_resize(font_data.fonts[i].gl_font, (uint16_t)(((float)font_data.fonts[i].font_size) * scale));
128             }
129         }
130     }
131 }
132 
133 
GLText_RenderStringLine(gl_text_line_p l)134 void GLText_RenderStringLine(gl_text_line_p l)
135 {
136     gl_tex_font_p gl_font = NULL;
137     gl_fontstyle_p style = NULL;
138 
139     if(l->show && (gl_font = GLText_GetFont(l->font_id)) && (style = GLText_GetFontStyle(l->style_id)))
140     {
141         GLfloat real_x = 0.0f, real_y = 0.0f;
142         int32_t x0, y0, x1, y1;
143         GLfloat shadow_color[4];
144         int32_t w_pt = (l->line_width * 64.0f + 0.5f);
145         int32_t dy = l->line_height * gl_font->font_size;
146         int n_lines = 1;
147         char *begin = l->text;
148         char *end = begin;
149 
150         shadow_color[0] = 0.0f;
151         shadow_color[1] = 0.0f;
152         shadow_color[2] = 0.0f;
153         shadow_color[3] = (float)style->font_color[3] * GUI_FONT_SHADOW_TRANSPARENCY;
154 
155         if(l->line_width > 0.0f)
156         {
157             int n_sym = 0;
158             n_lines = 0;
159             for(char *ch = glf_get_string_for_width(gl_font, l->text, w_pt, &n_sym); *begin; ch = glf_get_string_for_width(gl_font, ch, w_pt, &n_sym))
160             {
161                 if(!n_lines)
162                 {
163                     glf_get_string_bb(gl_font, l->text, n_sym, &x0, &y0, &x1, &y1);
164                 }
165                 ++n_lines;
166                 begin = ch;
167             }
168             begin = l->text;
169             x1 = x0 + w_pt;
170             y1 = y0 + n_lines * gl_font->font_size * l->line_height * 64.0f;
171         }
172         else
173         {
174             glf_get_string_bb(gl_font, l->text, -1, &x0, &y0, &x1, &y1);
175         }
176 
177         l->rect[0] = (GLfloat)x0 / 64.0f;
178         l->rect[1] = (GLfloat)y0 / 64.0f;
179         l->rect[2] = (GLfloat)x1 / 64.0f;
180         l->rect[3] = (GLfloat)y1 / 64.0f;
181 
182         switch(l->x_align)
183         {
184             case GLTEXT_ALIGN_LEFT:
185                 real_x = l->x;   // Used with center and right alignments.
186                 break;
187             case GLTEXT_ALIGN_RIGHT:
188                 real_x = (float)screen_width - (l->rect[2] - l->rect[0]) - l->x;
189                 break;
190             case GLTEXT_ALIGN_CENTER:
191                 real_x = l->x - 0.5f * (l->rect[2] - l->rect[0]);
192                 break;
193         }
194 
195         switch(l->y_align)
196         {
197             case GLTEXT_ALIGN_BOTTOM:
198                 real_y = l->y;
199                 break;
200             case GLTEXT_ALIGN_TOP:
201                 real_y = (float)screen_height - (l->rect[3] - l->rect[1]) - l->y;
202                 break;
203             case GLTEXT_ALIGN_CENTER:
204                 real_y = l->y - 0.5f * (l->rect[3] - l->rect[1]);
205                 break;
206         }
207 
208         if(style->rect)  // it is BS
209         {
210             BindWhiteTexture();
211             GLfloat x0 = l->rect[0] + real_x - style->rect_border * screen_width;
212             GLfloat y0 = l->rect[1] + real_y - style->rect_border * screen_height;
213             GLfloat x1 = l->rect[2] + real_x + style->rect_border * screen_width;
214             GLfloat y1 = l->rect[3] + real_y + style->rect_border * screen_height;
215             GLfloat *v, backgroundArray[32];
216 
217             v = backgroundArray;
218            *v++ = x0; *v++ = y0;
219             vec4_copy(v, style->rect_color);
220             v += 4;
221            *v++ = 0.0; *v++ = 0.0;
222 
223            *v++ = x1; *v++ = y0;
224             vec4_copy(v, style->rect_color);
225             v += 4;
226            *v++ = 0.0; *v++ = 0.0;
227 
228            *v++ = x1; *v++ = y1;
229             vec4_copy(v, style->rect_color);
230             v += 4;
231            *v++ = 0.0; *v++ = 0.0;
232 
233            *v++ = x0; *v++ = y1;
234             vec4_copy(v, style->rect_color);
235             v += 4;
236            *v++ = 0.0; *v++ = 0.0;
237 
238             qglVertexPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), backgroundArray);
239             qglColorPointer(4, GL_FLOAT, 8 * sizeof(GLfloat), backgroundArray + 2);
240             qglTexCoordPointer(2, GL_FLOAT, 8 * sizeof(GLfloat), backgroundArray + 6);
241             qglDrawArrays(GL_TRIANGLE_FAN, 0, 4);
242         }
243 
244 
245         for(int line = n_lines - 1; line >= 0; --line)
246         {
247             int n_sym = -1;
248             if(n_lines > 1)
249             {
250                 end = glf_get_string_for_width(gl_font, begin, w_pt, &n_sym);
251             }
252             if(style->shadowed)
253             {
254                 vec4_copy(gl_font->gl_font_color, shadow_color);
255                 glf_render_str(gl_font,
256                                (real_x + GUI_FONT_SHADOW_HORIZONTAL_SHIFT),
257                                (real_y + line * dy + GUI_FONT_SHADOW_VERTICAL_SHIFT),
258                                begin, n_sym);
259             }
260             vec4_copy(gl_font->gl_font_color, style->font_color);
261             glf_render_str(gl_font, real_x, real_y + line * dy, begin, n_sym);
262             begin = end;
263         }
264     }
265 }
266 
267 
GLText_RenderStrings()268 void GLText_RenderStrings()
269 {
270     gl_text_line_p l = font_data.gl_base_lines;
271 
272     qglBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
273     qglBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
274 
275     while(l)
276     {
277         GLText_RenderStringLine(l);
278         l = l->next;
279     }
280 
281     l = font_data.gl_temp_lines;
282     for(uint16_t i = 0; i < font_data.temp_lines_used; i++, l++)
283     {
284         if(l->show)
285         {
286             GLText_RenderStringLine(l);
287             l->show = 0;
288         }
289     }
290 
291     font_data.temp_lines_used = 0;
292 }
293 
294 
GLText_AddLine(gl_text_line_p line)295 void GLText_AddLine(gl_text_line_p line)
296 {
297     if(font_data.gl_base_lines == NULL)
298     {
299         font_data.gl_base_lines = line;
300         line->next = NULL;
301         line->prev = NULL;
302         return;
303     }
304 
305     line->prev = NULL;
306     line->next = font_data.gl_base_lines;
307     font_data.gl_base_lines->prev = line;
308     font_data.gl_base_lines = line;
309 }
310 
311 
312 // line must be in the list, otherway You crash engine!
GLText_DeleteLine(gl_text_line_p line)313 void GLText_DeleteLine(gl_text_line_p line)
314 {
315     if(line == font_data.gl_base_lines)
316     {
317         font_data.gl_base_lines = line->next;
318         if(font_data.gl_base_lines != NULL)
319         {
320             font_data.gl_base_lines->prev = NULL;
321         }
322         return;
323     }
324 
325     line->prev->next = line->next;
326     if(line->next)
327     {
328         line->next->prev = line->prev;
329     }
330 }
331 
332 /**
333  * For simple temporary lines rendering.
334  * Really all strings will be rendered in Gui_Render() function.
335  */
GLText_OutTextXY(GLfloat x,GLfloat y,const char * fmt,...)336 gl_text_line_p GLText_OutTextXY(GLfloat x, GLfloat y, const char *fmt, ...)
337 {
338     gl_text_line_p ret = NULL;
339     va_list argptr;
340 
341     va_start(argptr, fmt);
342     ret = GLText_VOutTextXY(x, y, fmt, argptr);
343     va_end(argptr);
344 
345     return ret;
346 }
347 
348 
GLText_VOutTextXY(GLfloat x,GLfloat y,const char * fmt,va_list argptr)349 gl_text_line_p GLText_VOutTextXY(GLfloat x, GLfloat y, const char *fmt, va_list argptr)
350 {
351     if(font_data.temp_lines_used < GLTEXT_MAX_TEMP_LINES - 1)
352     {
353         gl_text_line_p l = font_data.gl_temp_lines + font_data.temp_lines_used;
354 
355         l->font_id = FONT_SECONDARY;
356         l->style_id = FONTSTYLE_GENERIC;
357         l->line_width = -1.0f;
358         l->line_height = 1.75f;
359 
360         vsnprintf(l->text, GUI_LINE_DEFAULTSIZE, fmt, argptr);
361 
362         l->next = NULL;
363         l->prev = NULL;
364 
365         font_data.temp_lines_used++;
366 
367         l->x = x;
368         l->y = y;
369         l->x_align = GLTEXT_ALIGN_LEFT;
370         l->y_align = GLTEXT_ALIGN_BOTTOM;
371 
372         l->show = 1;
373         return l;
374     }
375 
376     return NULL;
377 }
378 
379 
GLText_AddFont(uint16_t index,uint16_t size,const char * path)380 int GLText_AddFont(uint16_t index, uint16_t size, const char* path)
381 {
382     if(index < font_data.max_fonts)
383     {
384         gl_tex_font_p new_font = glf_create_font(path, size);
385         if(new_font)
386         {
387             if(font_data.fonts[index].gl_font)
388             {
389                 glf_free_font(font_data.fonts[index].gl_font);
390             }
391             font_data.fonts[index].font_size = size;
392             font_data.fonts[index].gl_font = new_font;
393             return 1;
394         }
395     }
396 
397     return 0;
398 }
399 
400 
GLText_RemoveFont(uint16_t index)401 int GLText_RemoveFont(uint16_t index)
402 {
403     if((index < font_data.max_fonts) && (font_data.fonts[index].gl_font))
404     {
405         glf_free_font(font_data.fonts[index].gl_font);
406         font_data.fonts[index].gl_font = NULL;
407         return 1;
408     }
409 
410     return 0;
411 }
412 
413 
GLText_AddFontStyle(uint16_t index,GLfloat R,GLfloat G,GLfloat B,GLfloat A,uint8_t shadow,uint8_t rect,uint8_t rect_border,GLfloat rect_R,GLfloat rect_G,GLfloat rect_B,GLfloat rect_A)414 int GLText_AddFontStyle(uint16_t index,
415                      GLfloat R, GLfloat G, GLfloat B, GLfloat A,
416                      uint8_t shadow, uint8_t rect, uint8_t rect_border,
417                      GLfloat rect_R, GLfloat rect_G, GLfloat rect_B, GLfloat rect_A)
418 {
419     if(index < font_data.max_styles)
420     {
421         gl_fontstyle_p desired_style = font_data.styles + index;
422 
423         desired_style->rect_border   = rect_border;
424         desired_style->rect_color[0] = rect_R;
425         desired_style->rect_color[1] = rect_G;
426         desired_style->rect_color[2] = rect_B;
427         desired_style->rect_color[3] = rect_A;
428 
429         desired_style->font_color[0]  = R;
430         desired_style->font_color[1]  = G;
431         desired_style->font_color[2]  = B;
432         desired_style->font_color[3]  = A;
433 
434         desired_style->shadowed  = shadow;
435         desired_style->rect      = rect;
436         return 1;
437     }
438 
439     return 0;
440 }
441 
442 
GLText_RemoveFontStyle(uint16_t index)443 int GLText_RemoveFontStyle(uint16_t index)
444 {
445     if(index < font_data.max_styles)
446     {
447         font_data.styles[index].rect_color[0] = 1.0;
448         font_data.styles[index].rect_color[1] = 1.0;
449         font_data.styles[index].rect_color[2] = 1.0;
450         font_data.styles[index].rect_color[3] = 0.0;
451 
452         font_data.styles[index].font_color[0] = 0.0;
453         font_data.styles[index].font_color[1] = 0.0;
454         font_data.styles[index].font_color[2] = 0.0;
455         font_data.styles[index].font_color[3] = 1.0;
456 
457         font_data.styles[index].shadowed = 0x00;
458         font_data.styles[index].rect     = 0x00;
459         return 1;
460     }
461 
462     return 0;
463 }
464 
465 
GLText_GetFont(uint16_t index)466 gl_tex_font_p GLText_GetFont(uint16_t index)
467 {
468     if(index < font_data.max_fonts)
469     {
470         return font_data.fonts[index].gl_font;
471     }
472 
473     return NULL;
474 }
475 
476 
GLText_GetFontStyle(uint16_t index)477 gl_fontstyle_p GLText_GetFontStyle(uint16_t index)
478 {
479     if(index < font_data.max_styles)
480     {
481         return font_data.styles + index;
482     }
483 
484     return NULL;
485 }
486