rayfract

annotate 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
rev   line source
nuclear@2 1 #include <math.h>
nuclear@2 2 #include <ctype.h>
nuclear@2 3 #define GL_GLEXT_PROTOTYPES
nuclear@2 4 #if defined(__APPLE__) && defined(__MACH__)
nuclear@2 5 #include <GLUT/glut.h>
nuclear@2 6 #else
nuclear@2 7 #include <GL/glut.h>
nuclear@2 8 #endif
nuclear@2 9 #include <ft2build.h>
nuclear@2 10 #include FT_FREETYPE_H
nuclear@2 11 #include <vmath.h>
nuclear@2 12 #include "utktext.h"
nuclear@2 13 #include "utk_common.h"
nuclear@2 14
nuclear@2 15 #ifndef GL_BGRA
nuclear@2 16 #define GL_BGRA 0x80E1
nuclear@2 17 #endif
nuclear@2 18
nuclear@2 19 #ifndef GL_ABGR
nuclear@2 20 #define GL_ABGR 0x8000
nuclear@2 21 #endif
nuclear@2 22
nuclear@2 23 #ifdef UTK_BIG_ENDIAN
nuclear@2 24 #define PIXFMT GL_ABGR
nuclear@2 25 #else
nuclear@2 26 #define PIXFMT GL_BGRA
nuclear@2 27 #endif
nuclear@2 28
nuclear@2 29 #define MAX_CHARS 128
nuclear@2 30 #define MAX_IMG_WIDTH 1024
nuclear@2 31 #define SIZE_PIXELS(x) ((x) / 64)
nuclear@2 32
nuclear@2 33 struct Font {
nuclear@2 34 unsigned int tex_id;
nuclear@2 35 float scale; // this compensates if a higher res font is loaded in its stead, with a new CreateFont
nuclear@2 36 float line_adv; // vertical advance to go to the next line
nuclear@2 37 struct {
nuclear@2 38 Vector2 pos, size; // glyph position (from origin) and size in normalized coords [0, 1]
nuclear@2 39 float advance; // advance in normalized coords
nuclear@2 40 Vector2 tc_pos, tc_sz; // tex coord box pos/size
nuclear@2 41 } glyphs[MAX_CHARS];
nuclear@2 42 };
nuclear@2 43
nuclear@2 44 static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz);
nuclear@2 45 static void CleanUp();
nuclear@2 46
nuclear@2 47 static FT_Library ft;
nuclear@2 48 static Vector2 text_pos;
nuclear@2 49 static float text_size = 1.0;
nuclear@2 50 static Color text_color;
nuclear@2 51 static Font *act_fnt;
nuclear@2 52
nuclear@2 53 #if !defined(GLIBC) && !defined(__GLIBC__)
nuclear@2 54 float log2(float x) {
nuclear@2 55 float res = 0.0;
nuclear@2 56 while(x > 1.0) {
nuclear@2 57 x /= 2.0;
nuclear@2 58 res += 1.0;
nuclear@2 59 }
nuclear@2 60 return res;
nuclear@2 61 }
nuclear@2 62 #endif // _MSC_VER
nuclear@2 63
nuclear@2 64
nuclear@2 65 static inline int NextPow2(int x)
nuclear@2 66 {
nuclear@2 67 float lg2 = log2((float)x);
nuclear@2 68 return (int)pow(2.0f, (int)ceil(lg2));
nuclear@2 69 }
nuclear@2 70
nuclear@2 71
nuclear@2 72 unsigned int CreateFont(const char *fname, int font_size)
nuclear@2 73 {
nuclear@2 74 if(!ft) {
nuclear@2 75 if(FT_Init_FreeType(&ft) != 0) {
nuclear@2 76 fprintf(stderr, "failed to initialize freetype\n");
nuclear@2 77 return 0;
nuclear@2 78 }
nuclear@2 79 atexit(CleanUp);
nuclear@2 80 }
nuclear@2 81
nuclear@2 82 FT_Face face;
nuclear@2 83 if(FT_New_Face(ft, fname, 0, &face) != 0) {
nuclear@2 84 fprintf(stderr, "failed to load font: %s\n", fname);
nuclear@2 85 return 0;
nuclear@2 86 }
nuclear@2 87
nuclear@2 88 FT_Set_Pixel_Sizes(face, 0, font_size);
nuclear@2 89
nuclear@2 90 Font *fnt = new Font;
nuclear@2 91 int max_width = MAX_CHARS * SIZE_PIXELS(face->bbox.xMax - face->bbox.xMin);
nuclear@2 92 int foo_xsz = MAX_IMG_WIDTH;
nuclear@2 93 int foo_ysz = SIZE_PIXELS(face->bbox.yMax - face->bbox.yMin) * max_width / foo_xsz;
nuclear@2 94
nuclear@2 95 int tex_xsz = NextPow2(foo_xsz);
nuclear@2 96 int tex_ysz = NextPow2(foo_ysz);
nuclear@2 97
nuclear@2 98 unsigned int *img;
nuclear@2 99 img = new unsigned int[tex_xsz * tex_ysz];
nuclear@2 100 memset(img, 0, tex_xsz * tex_ysz * sizeof *img);
nuclear@2 101
nuclear@2 102 extern int xsz, ysz;
nuclear@2 103 int vport_xsz = xsz, vport_ysz = ysz;
nuclear@2 104
nuclear@2 105 int max_glyph_y = 0;
nuclear@2 106 int max_glyph_x = 0;
nuclear@2 107 for(int i=0; i<MAX_CHARS; i++) {
nuclear@2 108 FT_Load_Char(face, i, 0);
nuclear@2 109 int width = SIZE_PIXELS(face->glyph->metrics.width);
nuclear@2 110 int height = SIZE_PIXELS(face->glyph->metrics.height);
nuclear@2 111
nuclear@2 112 if(height > max_glyph_y) {
nuclear@2 113 max_glyph_y = height;
nuclear@2 114 }
nuclear@2 115
nuclear@2 116 if(width > max_glyph_x) {
nuclear@2 117 max_glyph_x = width;
nuclear@2 118 }
nuclear@2 119 }
nuclear@2 120
nuclear@2 121 int gx = 0, gy = 0;
nuclear@2 122 for(int i=0; i<MAX_CHARS; i++) {
nuclear@2 123 FT_Load_Char(face, i, FT_LOAD_RENDER);
nuclear@2 124 FT_GlyphSlot g = face->glyph;
nuclear@2 125
nuclear@2 126 int gwidth = SIZE_PIXELS(g->metrics.width);
nuclear@2 127 int gheight = SIZE_PIXELS(g->metrics.height);
nuclear@2 128
nuclear@2 129 if(gx > MAX_IMG_WIDTH - gwidth) {
nuclear@2 130 gx = 0;
nuclear@2 131 gy += max_glyph_y;
nuclear@2 132 }
nuclear@2 133
nuclear@2 134 BlitFontGlyph(fnt, gx, gy, g, img, tex_xsz, tex_ysz);
nuclear@2 135 fnt->scale = 1.0;
nuclear@2 136 fnt->line_adv = (float)SIZE_PIXELS(g->metrics.vertAdvance) / (float)vport_ysz;
nuclear@2 137 fnt->glyphs[i].tc_pos.x = (float)gx / (float)tex_xsz;
nuclear@2 138 fnt->glyphs[i].tc_pos.y = (float)gy / (float)tex_ysz;
nuclear@2 139 fnt->glyphs[i].tc_sz.x = (float)gwidth / (float)tex_xsz;
nuclear@2 140 fnt->glyphs[i].tc_sz.y = (float)gheight / (float)tex_ysz;
nuclear@2 141 fnt->glyphs[i].size.x = (float)gwidth / (float)vport_xsz;
nuclear@2 142 fnt->glyphs[i].size.y = (float)gheight / (float)vport_ysz;
nuclear@2 143 fnt->glyphs[i].pos.x = (float)SIZE_PIXELS(g->metrics.horiBearingX) / (float)vport_xsz;
nuclear@2 144 fnt->glyphs[i].pos.y = -(float)SIZE_PIXELS(g->metrics.horiBearingY) / (float)vport_ysz;
nuclear@2 145 fnt->glyphs[i].advance = (float)SIZE_PIXELS(g->metrics.horiAdvance) / (float)vport_xsz;
nuclear@2 146
nuclear@2 147 gx += gwidth;
nuclear@2 148 }
nuclear@2 149
nuclear@2 150 FT_Done_Face(face);
nuclear@2 151
nuclear@2 152 glGenTextures(1, &fnt->tex_id);
nuclear@2 153 glBindTexture(GL_TEXTURE_2D, fnt->tex_id);
nuclear@2 154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
nuclear@2 155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
nuclear@2 156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
nuclear@2 157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
nuclear@2 158 glTexImage2D(GL_TEXTURE_2D, 0, 4, tex_xsz, tex_ysz, 0, PIXFMT, GL_UNSIGNED_BYTE, img);
nuclear@2 159
nuclear@2 160 #ifdef DUMP_FONT_IMG
nuclear@2 161 FILE *fp;
nuclear@2 162 unsigned int *ptr = img;
nuclear@2 163
nuclear@2 164 if((fp = fopen("fnt.ppm", "wb"))) {
nuclear@2 165 fprintf(fp, "P6\n%d %d\n255\n", tex_xsz, tex_ysz);
nuclear@2 166
nuclear@2 167 for(int i=0; i<tex_xsz * tex_ysz; i++) {
nuclear@2 168 fputc((*ptr >> 24) & 0xff, fp);
nuclear@2 169 fputc((*ptr >> 24) & 0xff, fp);
nuclear@2 170 fputc((*ptr >> 24) & 0xff, fp);
nuclear@2 171 ptr++;
nuclear@2 172 }
nuclear@2 173 fclose(fp);
nuclear@2 174 }
nuclear@2 175 #endif
nuclear@2 176
nuclear@2 177 delete [] img;
nuclear@2 178
nuclear@2 179 act_fnt = fnt;
nuclear@2 180
nuclear@2 181 return 1;
nuclear@2 182 }
nuclear@2 183
nuclear@2 184 void DeleteFont(unsigned int fid)
nuclear@2 185 {
nuclear@2 186 }
nuclear@2 187
nuclear@2 188 unsigned int GetFont(const char *fname, int sz)
nuclear@2 189 {
nuclear@2 190 return 1;
nuclear@2 191 }
nuclear@2 192
nuclear@2 193 bool BindFont(unsigned int fid)
nuclear@2 194 {
nuclear@2 195 return true;
nuclear@2 196 }
nuclear@2 197
nuclear@2 198 void SetTextPos(const Vector2 &pos)
nuclear@2 199 {
nuclear@2 200 text_pos = pos;
nuclear@2 201 }
nuclear@2 202
nuclear@2 203 Vector2 GetTextPos()
nuclear@2 204 {
nuclear@2 205 return text_pos;
nuclear@2 206 }
nuclear@2 207
nuclear@2 208 void TextLineAdvance(int adv)
nuclear@2 209 {
nuclear@2 210 text_pos.y += (float)adv * act_fnt->line_adv;
nuclear@2 211 }
nuclear@2 212
nuclear@2 213 void TextCRet()
nuclear@2 214 {
nuclear@2 215 text_pos.x = 0.0;
nuclear@2 216 }
nuclear@2 217
nuclear@2 218 void SetTextSize(float sz)
nuclear@2 219 {
nuclear@2 220 text_size = sz;
nuclear@2 221 }
nuclear@2 222
nuclear@2 223 float GetTextSize()
nuclear@2 224 {
nuclear@2 225 return text_size;
nuclear@2 226 }
nuclear@2 227
nuclear@2 228 void SetTextColor(const Color &col)
nuclear@2 229 {
nuclear@2 230 text_color = col;
nuclear@2 231 }
nuclear@2 232
nuclear@2 233 Color GetTextColor()
nuclear@2 234 {
nuclear@2 235 return text_color;
nuclear@2 236 }
nuclear@2 237
nuclear@2 238 static void ImOverlay(const Vector2 &v1, const Vector2 &v2, const Color &col, unsigned int tex)
nuclear@2 239 {
nuclear@2 240 float l = v1.x * 2.0f - 1.0f;
nuclear@2 241 float r = v2.x * 2.0f - 1.0f;
nuclear@2 242 float u = -v1.y * 2.0f + 1.0f;
nuclear@2 243 float d = -v2.y * 2.0f + 1.0f;
nuclear@2 244
nuclear@2 245 glMatrixMode(GL_PROJECTION);
nuclear@2 246 glPushMatrix();
nuclear@2 247 glLoadIdentity();
nuclear@2 248 glMatrixMode(GL_MODELVIEW);
nuclear@2 249 glPushMatrix();
nuclear@2 250 glLoadIdentity();
nuclear@2 251
nuclear@2 252 glBindTexture(GL_TEXTURE_2D, tex);
nuclear@2 253
nuclear@2 254 glDisable(GL_DEPTH_TEST);
nuclear@2 255 glDisable(GL_LIGHTING);
nuclear@2 256 glDisable(GL_CULL_FACE);
nuclear@2 257
nuclear@2 258 glBegin(GL_QUADS);
nuclear@2 259 glColor4f(col.r, col.g, col.b, col.a);
nuclear@2 260 glTexCoord2f(0, 0);
nuclear@2 261 glVertex2f(l, u);
nuclear@2 262 glTexCoord2f(1, 0);
nuclear@2 263 glVertex2f(r, u);
nuclear@2 264 glTexCoord2f(1, 1);
nuclear@2 265 glVertex2f(r, d);
nuclear@2 266 glTexCoord2f(0, 1);
nuclear@2 267 glVertex2f(l, d);
nuclear@2 268 glEnd();
nuclear@2 269
nuclear@2 270 glEnable(GL_LIGHTING);
nuclear@2 271 glEnable(GL_DEPTH_TEST);
nuclear@2 272
nuclear@2 273 glMatrixMode(GL_PROJECTION);
nuclear@2 274 glPopMatrix();
nuclear@2 275 glMatrixMode(GL_MODELVIEW);
nuclear@2 276 glPopMatrix();
nuclear@2 277 }
nuclear@2 278
nuclear@2 279 float PrintChar(char c)
nuclear@2 280 {
nuclear@2 281 // get texture coordinates for the glyph, and construct the texture matrix
nuclear@2 282 float tx = act_fnt->glyphs[(int)c].tc_pos.x;
nuclear@2 283 float ty = act_fnt->glyphs[(int)c].tc_pos.y;
nuclear@2 284 float sx = act_fnt->glyphs[(int)c].tc_sz.x;
nuclear@2 285 float sy = act_fnt->glyphs[(int)c].tc_sz.y;
nuclear@2 286
nuclear@2 287 float mat[] = {
nuclear@2 288 sx, 0, tx, 0,
nuclear@2 289 0, sy, ty, 0,
nuclear@2 290 0, 0, 1, 0,
nuclear@2 291 0, 0, 0, 1
nuclear@2 292 };
nuclear@2 293
nuclear@2 294 glMatrixMode(GL_TEXTURE);
nuclear@2 295 glPushMatrix();
nuclear@2 296 glLoadMatrixf(mat);
nuclear@2 297
nuclear@2 298 Vector2 pos = text_pos + act_fnt->glyphs[(int)c].pos * act_fnt->scale;
nuclear@2 299 ImOverlay(pos, pos + act_fnt->glyphs[(int)c].size * act_fnt->scale, text_color, act_fnt->tex_id);
nuclear@2 300
nuclear@2 301 glMatrixMode(GL_TEXTURE);
nuclear@2 302 glPopMatrix();
nuclear@2 303
nuclear@2 304 return act_fnt->glyphs[(int)c].advance * act_fnt->scale;
nuclear@2 305 }
nuclear@2 306
nuclear@2 307 // this function contains the preamble of all block text drawing functions (i.e. not printchar above)
nuclear@2 308 static void PreDraw()
nuclear@2 309 {
nuclear@2 310 glMatrixMode(GL_PROJECTION);
nuclear@2 311 glPushMatrix();
nuclear@2 312 /*glLoadTransposeMatrixf(OrthoProj(2, 2, 0, 10).m);*/
nuclear@2 313 glLoadIdentity();
nuclear@2 314 glMatrixMode(GL_MODELVIEW);
nuclear@2 315 glPushMatrix();
nuclear@2 316 glLoadIdentity();
nuclear@2 317 glMatrixMode(GL_TEXTURE);
nuclear@2 318 glPushMatrix();
nuclear@2 319 glLoadIdentity();
nuclear@2 320
nuclear@2 321 glEnable(GL_TEXTURE_2D);
nuclear@2 322 glBindTexture(GL_TEXTURE_2D, act_fnt->tex_id);
nuclear@2 323
nuclear@2 324 glBegin(GL_QUADS);
nuclear@2 325 glColor4f(text_color.r, text_color.g, text_color.b, text_color.a);
nuclear@2 326 }
nuclear@2 327
nuclear@2 328 static void PostDraw()
nuclear@2 329 {
nuclear@2 330 glEnd();
nuclear@2 331
nuclear@2 332 glDisable(GL_TEXTURE_2D);
nuclear@2 333
nuclear@2 334 glMatrixMode(GL_TEXTURE);
nuclear@2 335 glPopMatrix();
nuclear@2 336 glMatrixMode(GL_MODELVIEW);
nuclear@2 337 glPopMatrix();
nuclear@2 338 glMatrixMode(GL_PROJECTION);
nuclear@2 339 glPopMatrix();
nuclear@2 340 }
nuclear@2 341
nuclear@2 342 float PrintString(const char *str, bool standalone)
nuclear@2 343 {
nuclear@2 344 if(standalone) PreDraw();
nuclear@2 345
nuclear@2 346 float start_x = text_pos.x;
nuclear@2 347 while(*str) {
nuclear@2 348 float tx = act_fnt->glyphs[(int)*str].tc_pos.x;
nuclear@2 349 float ty = act_fnt->glyphs[(int)*str].tc_pos.y;
nuclear@2 350 float sx = act_fnt->glyphs[(int)*str].tc_sz.x;
nuclear@2 351 float sy = act_fnt->glyphs[(int)*str].tc_sz.y;
nuclear@2 352
nuclear@2 353 Vector2 tc1 = Vector2(tx, ty);
nuclear@2 354 Vector2 tc2 = Vector2(tx + sx, ty + sy);
nuclear@2 355
nuclear@2 356 Vector2 v1 = text_pos + act_fnt->glyphs[(int)*str].pos * act_fnt->scale * text_size;
nuclear@2 357 Vector2 v2 = v1 + act_fnt->glyphs[(int)*str].size * act_fnt->scale * text_size;
nuclear@2 358 float l = v1.x * 2.0f - 1.0f;
nuclear@2 359 float r = v2.x * 2.0f - 1.0f;
nuclear@2 360 float u = -v1.y * 2.0f + 1.0f;
nuclear@2 361 float d = -v2.y * 2.0f + 1.0f;
nuclear@2 362
nuclear@2 363 glTexCoord2f(tc1.x, tc1.y);
nuclear@2 364 glVertex2f(l, u);
nuclear@2 365 glTexCoord2f(tc2.x, tc1.y);
nuclear@2 366 glVertex2f(r, u);
nuclear@2 367 glTexCoord2f(tc2.x, tc2.y);
nuclear@2 368 glVertex2f(r, d);
nuclear@2 369 glTexCoord2f(tc1.x, tc2.y);
nuclear@2 370 glVertex2f(l, d);
nuclear@2 371
nuclear@2 372 text_pos.x += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size;
nuclear@2 373 }
nuclear@2 374
nuclear@2 375 if(standalone) PostDraw();
nuclear@2 376 return text_pos.x - start_x;
nuclear@2 377 }
nuclear@2 378
nuclear@2 379 void PrintStringLines(const char **str, int lines)
nuclear@2 380 {
nuclear@2 381 PreDraw();
nuclear@2 382
nuclear@2 383 while(lines-- > 0) {
nuclear@2 384 PrintString(*str++, false);
nuclear@2 385 TextLineAdvance();
nuclear@2 386 TextCRet();
nuclear@2 387 }
nuclear@2 388
nuclear@2 389 PostDraw();
nuclear@2 390 }
nuclear@2 391
nuclear@2 392 static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz)
nuclear@2 393 {
nuclear@2 394 if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) {
nuclear@2 395 fprintf(stderr, "BlitFontGlyph: unsupported pixel mode: %d\n", glyph->bitmap.pixel_mode);
nuclear@2 396 return;
nuclear@2 397 }
nuclear@2 398
nuclear@2 399 unsigned int *dest = img + y * xsz + x;
nuclear@2 400 unsigned char *src = glyph->bitmap.buffer;
nuclear@2 401
nuclear@2 402 for(int j=0; j<glyph->bitmap.rows; j++) {
nuclear@2 403 for(int i=0; i<glyph->bitmap.width; i++) {
nuclear@2 404 dest[i] = 0x00ffffff | ((unsigned int)src[i] << 24);
nuclear@2 405 }
nuclear@2 406 dest += xsz;
nuclear@2 407 src += glyph->bitmap.pitch;
nuclear@2 408 }
nuclear@2 409 }
nuclear@2 410
nuclear@2 411 static void CleanUp()
nuclear@2 412 {
nuclear@2 413 FT_Done_FreeType(ft);
nuclear@2 414 }
nuclear@2 415
nuclear@2 416 float GetMaxDescent()
nuclear@2 417 {
nuclear@2 418 Font *fnt = act_fnt;
nuclear@2 419
nuclear@2 420 float max_descent = 0.0f;
nuclear@2 421
nuclear@2 422 for(int i=0; i<MAX_CHARS; i++) {
nuclear@2 423 float des = fnt->glyphs[i].pos.y + fnt->glyphs[i].size.y;
nuclear@2 424 if(isprint(i) && des > max_descent) {
nuclear@2 425 max_descent = des;
nuclear@2 426 }
nuclear@2 427 }
nuclear@2 428
nuclear@2 429 return max_descent;
nuclear@2 430 }
nuclear@2 431
nuclear@2 432 float GetLineAdvance()
nuclear@2 433 {
nuclear@2 434 return act_fnt->line_adv;
nuclear@2 435 }
nuclear@2 436
nuclear@2 437 float GetTextWidth(const char *str)
nuclear@2 438 {
nuclear@2 439 float width = 0;
nuclear@2 440 while(*str) {
nuclear@2 441 width += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size;
nuclear@2 442 }
nuclear@2 443 return width;
nuclear@2 444 }