rayfract
diff src/utktext.cc @ 2:87b6a11c920b
added gui stuff
author | John Tsiombikas <nuclear@siggraph.org> |
---|---|
date | Tue, 26 Oct 2010 08:49:09 +0300 |
parents | |
children | bf1d56975cc9 |
line diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/utktext.cc Tue Oct 26 08:49:09 2010 +0300 1.3 @@ -0,0 +1,444 @@ 1.4 +#include <math.h> 1.5 +#include <ctype.h> 1.6 +#define GL_GLEXT_PROTOTYPES 1.7 +#if defined(__APPLE__) && defined(__MACH__) 1.8 +#include <GLUT/glut.h> 1.9 +#else 1.10 +#include <GL/glut.h> 1.11 +#endif 1.12 +#include <ft2build.h> 1.13 +#include FT_FREETYPE_H 1.14 +#include <vmath.h> 1.15 +#include "utktext.h" 1.16 +#include "utk_common.h" 1.17 + 1.18 +#ifndef GL_BGRA 1.19 +#define GL_BGRA 0x80E1 1.20 +#endif 1.21 + 1.22 +#ifndef GL_ABGR 1.23 +#define GL_ABGR 0x8000 1.24 +#endif 1.25 + 1.26 +#ifdef UTK_BIG_ENDIAN 1.27 +#define PIXFMT GL_ABGR 1.28 +#else 1.29 +#define PIXFMT GL_BGRA 1.30 +#endif 1.31 + 1.32 +#define MAX_CHARS 128 1.33 +#define MAX_IMG_WIDTH 1024 1.34 +#define SIZE_PIXELS(x) ((x) / 64) 1.35 + 1.36 +struct Font { 1.37 + unsigned int tex_id; 1.38 + float scale; // this compensates if a higher res font is loaded in its stead, with a new CreateFont 1.39 + float line_adv; // vertical advance to go to the next line 1.40 + struct { 1.41 + Vector2 pos, size; // glyph position (from origin) and size in normalized coords [0, 1] 1.42 + float advance; // advance in normalized coords 1.43 + Vector2 tc_pos, tc_sz; // tex coord box pos/size 1.44 + } glyphs[MAX_CHARS]; 1.45 +}; 1.46 + 1.47 +static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz); 1.48 +static void CleanUp(); 1.49 + 1.50 +static FT_Library ft; 1.51 +static Vector2 text_pos; 1.52 +static float text_size = 1.0; 1.53 +static Color text_color; 1.54 +static Font *act_fnt; 1.55 + 1.56 +#if !defined(GLIBC) && !defined(__GLIBC__) 1.57 +float log2(float x) { 1.58 + float res = 0.0; 1.59 + while(x > 1.0) { 1.60 + x /= 2.0; 1.61 + res += 1.0; 1.62 + } 1.63 + return res; 1.64 +} 1.65 +#endif // _MSC_VER 1.66 + 1.67 + 1.68 +static inline int NextPow2(int x) 1.69 +{ 1.70 + float lg2 = log2((float)x); 1.71 + return (int)pow(2.0f, (int)ceil(lg2)); 1.72 +} 1.73 + 1.74 + 1.75 +unsigned int CreateFont(const char *fname, int font_size) 1.76 +{ 1.77 + if(!ft) { 1.78 + if(FT_Init_FreeType(&ft) != 0) { 1.79 + fprintf(stderr, "failed to initialize freetype\n"); 1.80 + return 0; 1.81 + } 1.82 + atexit(CleanUp); 1.83 + } 1.84 + 1.85 + FT_Face face; 1.86 + if(FT_New_Face(ft, fname, 0, &face) != 0) { 1.87 + fprintf(stderr, "failed to load font: %s\n", fname); 1.88 + return 0; 1.89 + } 1.90 + 1.91 + FT_Set_Pixel_Sizes(face, 0, font_size); 1.92 + 1.93 + Font *fnt = new Font; 1.94 + int max_width = MAX_CHARS * SIZE_PIXELS(face->bbox.xMax - face->bbox.xMin); 1.95 + int foo_xsz = MAX_IMG_WIDTH; 1.96 + int foo_ysz = SIZE_PIXELS(face->bbox.yMax - face->bbox.yMin) * max_width / foo_xsz; 1.97 + 1.98 + int tex_xsz = NextPow2(foo_xsz); 1.99 + int tex_ysz = NextPow2(foo_ysz); 1.100 + 1.101 + unsigned int *img; 1.102 + img = new unsigned int[tex_xsz * tex_ysz]; 1.103 + memset(img, 0, tex_xsz * tex_ysz * sizeof *img); 1.104 + 1.105 + extern int xsz, ysz; 1.106 + int vport_xsz = xsz, vport_ysz = ysz; 1.107 + 1.108 + int max_glyph_y = 0; 1.109 + int max_glyph_x = 0; 1.110 + for(int i=0; i<MAX_CHARS; i++) { 1.111 + FT_Load_Char(face, i, 0); 1.112 + int width = SIZE_PIXELS(face->glyph->metrics.width); 1.113 + int height = SIZE_PIXELS(face->glyph->metrics.height); 1.114 + 1.115 + if(height > max_glyph_y) { 1.116 + max_glyph_y = height; 1.117 + } 1.118 + 1.119 + if(width > max_glyph_x) { 1.120 + max_glyph_x = width; 1.121 + } 1.122 + } 1.123 + 1.124 + int gx = 0, gy = 0; 1.125 + for(int i=0; i<MAX_CHARS; i++) { 1.126 + FT_Load_Char(face, i, FT_LOAD_RENDER); 1.127 + FT_GlyphSlot g = face->glyph; 1.128 + 1.129 + int gwidth = SIZE_PIXELS(g->metrics.width); 1.130 + int gheight = SIZE_PIXELS(g->metrics.height); 1.131 + 1.132 + if(gx > MAX_IMG_WIDTH - gwidth) { 1.133 + gx = 0; 1.134 + gy += max_glyph_y; 1.135 + } 1.136 + 1.137 + BlitFontGlyph(fnt, gx, gy, g, img, tex_xsz, tex_ysz); 1.138 + fnt->scale = 1.0; 1.139 + fnt->line_adv = (float)SIZE_PIXELS(g->metrics.vertAdvance) / (float)vport_ysz; 1.140 + fnt->glyphs[i].tc_pos.x = (float)gx / (float)tex_xsz; 1.141 + fnt->glyphs[i].tc_pos.y = (float)gy / (float)tex_ysz; 1.142 + fnt->glyphs[i].tc_sz.x = (float)gwidth / (float)tex_xsz; 1.143 + fnt->glyphs[i].tc_sz.y = (float)gheight / (float)tex_ysz; 1.144 + fnt->glyphs[i].size.x = (float)gwidth / (float)vport_xsz; 1.145 + fnt->glyphs[i].size.y = (float)gheight / (float)vport_ysz; 1.146 + fnt->glyphs[i].pos.x = (float)SIZE_PIXELS(g->metrics.horiBearingX) / (float)vport_xsz; 1.147 + fnt->glyphs[i].pos.y = -(float)SIZE_PIXELS(g->metrics.horiBearingY) / (float)vport_ysz; 1.148 + fnt->glyphs[i].advance = (float)SIZE_PIXELS(g->metrics.horiAdvance) / (float)vport_xsz; 1.149 + 1.150 + gx += gwidth; 1.151 + } 1.152 + 1.153 + FT_Done_Face(face); 1.154 + 1.155 + glGenTextures(1, &fnt->tex_id); 1.156 + glBindTexture(GL_TEXTURE_2D, fnt->tex_id); 1.157 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); 1.158 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 1.159 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 1.160 + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 1.161 + glTexImage2D(GL_TEXTURE_2D, 0, 4, tex_xsz, tex_ysz, 0, PIXFMT, GL_UNSIGNED_BYTE, img); 1.162 + 1.163 +#ifdef DUMP_FONT_IMG 1.164 + FILE *fp; 1.165 + unsigned int *ptr = img; 1.166 + 1.167 + if((fp = fopen("fnt.ppm", "wb"))) { 1.168 + fprintf(fp, "P6\n%d %d\n255\n", tex_xsz, tex_ysz); 1.169 + 1.170 + for(int i=0; i<tex_xsz * tex_ysz; i++) { 1.171 + fputc((*ptr >> 24) & 0xff, fp); 1.172 + fputc((*ptr >> 24) & 0xff, fp); 1.173 + fputc((*ptr >> 24) & 0xff, fp); 1.174 + ptr++; 1.175 + } 1.176 + fclose(fp); 1.177 + } 1.178 +#endif 1.179 + 1.180 + delete [] img; 1.181 + 1.182 + act_fnt = fnt; 1.183 + 1.184 + return 1; 1.185 +} 1.186 + 1.187 +void DeleteFont(unsigned int fid) 1.188 +{ 1.189 +} 1.190 + 1.191 +unsigned int GetFont(const char *fname, int sz) 1.192 +{ 1.193 + return 1; 1.194 +} 1.195 + 1.196 +bool BindFont(unsigned int fid) 1.197 +{ 1.198 + return true; 1.199 +} 1.200 + 1.201 +void SetTextPos(const Vector2 &pos) 1.202 +{ 1.203 + text_pos = pos; 1.204 +} 1.205 + 1.206 +Vector2 GetTextPos() 1.207 +{ 1.208 + return text_pos; 1.209 +} 1.210 + 1.211 +void TextLineAdvance(int adv) 1.212 +{ 1.213 + text_pos.y += (float)adv * act_fnt->line_adv; 1.214 +} 1.215 + 1.216 +void TextCRet() 1.217 +{ 1.218 + text_pos.x = 0.0; 1.219 +} 1.220 + 1.221 +void SetTextSize(float sz) 1.222 +{ 1.223 + text_size = sz; 1.224 +} 1.225 + 1.226 +float GetTextSize() 1.227 +{ 1.228 + return text_size; 1.229 +} 1.230 + 1.231 +void SetTextColor(const Color &col) 1.232 +{ 1.233 + text_color = col; 1.234 +} 1.235 + 1.236 +Color GetTextColor() 1.237 +{ 1.238 + return text_color; 1.239 +} 1.240 + 1.241 +static void ImOverlay(const Vector2 &v1, const Vector2 &v2, const Color &col, unsigned int tex) 1.242 +{ 1.243 + float l = v1.x * 2.0f - 1.0f; 1.244 + float r = v2.x * 2.0f - 1.0f; 1.245 + float u = -v1.y * 2.0f + 1.0f; 1.246 + float d = -v2.y * 2.0f + 1.0f; 1.247 + 1.248 + glMatrixMode(GL_PROJECTION); 1.249 + glPushMatrix(); 1.250 + glLoadIdentity(); 1.251 + glMatrixMode(GL_MODELVIEW); 1.252 + glPushMatrix(); 1.253 + glLoadIdentity(); 1.254 + 1.255 + glBindTexture(GL_TEXTURE_2D, tex); 1.256 + 1.257 + glDisable(GL_DEPTH_TEST); 1.258 + glDisable(GL_LIGHTING); 1.259 + glDisable(GL_CULL_FACE); 1.260 + 1.261 + glBegin(GL_QUADS); 1.262 + glColor4f(col.r, col.g, col.b, col.a); 1.263 + glTexCoord2f(0, 0); 1.264 + glVertex2f(l, u); 1.265 + glTexCoord2f(1, 0); 1.266 + glVertex2f(r, u); 1.267 + glTexCoord2f(1, 1); 1.268 + glVertex2f(r, d); 1.269 + glTexCoord2f(0, 1); 1.270 + glVertex2f(l, d); 1.271 + glEnd(); 1.272 + 1.273 + glEnable(GL_LIGHTING); 1.274 + glEnable(GL_DEPTH_TEST); 1.275 + 1.276 + glMatrixMode(GL_PROJECTION); 1.277 + glPopMatrix(); 1.278 + glMatrixMode(GL_MODELVIEW); 1.279 + glPopMatrix(); 1.280 +} 1.281 + 1.282 +float PrintChar(char c) 1.283 +{ 1.284 + // get texture coordinates for the glyph, and construct the texture matrix 1.285 + float tx = act_fnt->glyphs[(int)c].tc_pos.x; 1.286 + float ty = act_fnt->glyphs[(int)c].tc_pos.y; 1.287 + float sx = act_fnt->glyphs[(int)c].tc_sz.x; 1.288 + float sy = act_fnt->glyphs[(int)c].tc_sz.y; 1.289 + 1.290 + float mat[] = { 1.291 + sx, 0, tx, 0, 1.292 + 0, sy, ty, 0, 1.293 + 0, 0, 1, 0, 1.294 + 0, 0, 0, 1 1.295 + }; 1.296 + 1.297 + glMatrixMode(GL_TEXTURE); 1.298 + glPushMatrix(); 1.299 + glLoadMatrixf(mat); 1.300 + 1.301 + Vector2 pos = text_pos + act_fnt->glyphs[(int)c].pos * act_fnt->scale; 1.302 + ImOverlay(pos, pos + act_fnt->glyphs[(int)c].size * act_fnt->scale, text_color, act_fnt->tex_id); 1.303 + 1.304 + glMatrixMode(GL_TEXTURE); 1.305 + glPopMatrix(); 1.306 + 1.307 + return act_fnt->glyphs[(int)c].advance * act_fnt->scale; 1.308 +} 1.309 + 1.310 +// this function contains the preamble of all block text drawing functions (i.e. not printchar above) 1.311 +static void PreDraw() 1.312 +{ 1.313 + glMatrixMode(GL_PROJECTION); 1.314 + glPushMatrix(); 1.315 + /*glLoadTransposeMatrixf(OrthoProj(2, 2, 0, 10).m);*/ 1.316 + glLoadIdentity(); 1.317 + glMatrixMode(GL_MODELVIEW); 1.318 + glPushMatrix(); 1.319 + glLoadIdentity(); 1.320 + glMatrixMode(GL_TEXTURE); 1.321 + glPushMatrix(); 1.322 + glLoadIdentity(); 1.323 + 1.324 + glEnable(GL_TEXTURE_2D); 1.325 + glBindTexture(GL_TEXTURE_2D, act_fnt->tex_id); 1.326 + 1.327 + glBegin(GL_QUADS); 1.328 + glColor4f(text_color.r, text_color.g, text_color.b, text_color.a); 1.329 +} 1.330 + 1.331 +static void PostDraw() 1.332 +{ 1.333 + glEnd(); 1.334 + 1.335 + glDisable(GL_TEXTURE_2D); 1.336 + 1.337 + glMatrixMode(GL_TEXTURE); 1.338 + glPopMatrix(); 1.339 + glMatrixMode(GL_MODELVIEW); 1.340 + glPopMatrix(); 1.341 + glMatrixMode(GL_PROJECTION); 1.342 + glPopMatrix(); 1.343 +} 1.344 + 1.345 +float PrintString(const char *str, bool standalone) 1.346 +{ 1.347 + if(standalone) PreDraw(); 1.348 + 1.349 + float start_x = text_pos.x; 1.350 + while(*str) { 1.351 + float tx = act_fnt->glyphs[(int)*str].tc_pos.x; 1.352 + float ty = act_fnt->glyphs[(int)*str].tc_pos.y; 1.353 + float sx = act_fnt->glyphs[(int)*str].tc_sz.x; 1.354 + float sy = act_fnt->glyphs[(int)*str].tc_sz.y; 1.355 + 1.356 + Vector2 tc1 = Vector2(tx, ty); 1.357 + Vector2 tc2 = Vector2(tx + sx, ty + sy); 1.358 + 1.359 + Vector2 v1 = text_pos + act_fnt->glyphs[(int)*str].pos * act_fnt->scale * text_size; 1.360 + Vector2 v2 = v1 + act_fnt->glyphs[(int)*str].size * act_fnt->scale * text_size; 1.361 + float l = v1.x * 2.0f - 1.0f; 1.362 + float r = v2.x * 2.0f - 1.0f; 1.363 + float u = -v1.y * 2.0f + 1.0f; 1.364 + float d = -v2.y * 2.0f + 1.0f; 1.365 + 1.366 + glTexCoord2f(tc1.x, tc1.y); 1.367 + glVertex2f(l, u); 1.368 + glTexCoord2f(tc2.x, tc1.y); 1.369 + glVertex2f(r, u); 1.370 + glTexCoord2f(tc2.x, tc2.y); 1.371 + glVertex2f(r, d); 1.372 + glTexCoord2f(tc1.x, tc2.y); 1.373 + glVertex2f(l, d); 1.374 + 1.375 + text_pos.x += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size; 1.376 + } 1.377 + 1.378 + if(standalone) PostDraw(); 1.379 + return text_pos.x - start_x; 1.380 +} 1.381 + 1.382 +void PrintStringLines(const char **str, int lines) 1.383 +{ 1.384 + PreDraw(); 1.385 + 1.386 + while(lines-- > 0) { 1.387 + PrintString(*str++, false); 1.388 + TextLineAdvance(); 1.389 + TextCRet(); 1.390 + } 1.391 + 1.392 + PostDraw(); 1.393 +} 1.394 + 1.395 +static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz) 1.396 +{ 1.397 + if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) { 1.398 + fprintf(stderr, "BlitFontGlyph: unsupported pixel mode: %d\n", glyph->bitmap.pixel_mode); 1.399 + return; 1.400 + } 1.401 + 1.402 + unsigned int *dest = img + y * xsz + x; 1.403 + unsigned char *src = glyph->bitmap.buffer; 1.404 + 1.405 + for(int j=0; j<glyph->bitmap.rows; j++) { 1.406 + for(int i=0; i<glyph->bitmap.width; i++) { 1.407 + dest[i] = 0x00ffffff | ((unsigned int)src[i] << 24); 1.408 + } 1.409 + dest += xsz; 1.410 + src += glyph->bitmap.pitch; 1.411 + } 1.412 +} 1.413 + 1.414 +static void CleanUp() 1.415 +{ 1.416 + FT_Done_FreeType(ft); 1.417 +} 1.418 + 1.419 +float GetMaxDescent() 1.420 +{ 1.421 + Font *fnt = act_fnt; 1.422 + 1.423 + float max_descent = 0.0f; 1.424 + 1.425 + for(int i=0; i<MAX_CHARS; i++) { 1.426 + float des = fnt->glyphs[i].pos.y + fnt->glyphs[i].size.y; 1.427 + if(isprint(i) && des > max_descent) { 1.428 + max_descent = des; 1.429 + } 1.430 + } 1.431 + 1.432 + return max_descent; 1.433 +} 1.434 + 1.435 +float GetLineAdvance() 1.436 +{ 1.437 + return act_fnt->line_adv; 1.438 +} 1.439 + 1.440 +float GetTextWidth(const char *str) 1.441 +{ 1.442 + float width = 0; 1.443 + while(*str) { 1.444 + width += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size; 1.445 + } 1.446 + return width; 1.447 +}