nuclear@2: #include nuclear@2: #include nuclear@2: #define GL_GLEXT_PROTOTYPES nuclear@2: #if defined(__APPLE__) && defined(__MACH__) nuclear@2: #include nuclear@2: #else nuclear@2: #include nuclear@2: #endif nuclear@2: #include nuclear@2: #include FT_FREETYPE_H nuclear@2: #include "utktext.h" nuclear@2: #include "utk_common.h" nuclear@3: #include "vmath.h" nuclear@2: nuclear@2: #ifndef GL_BGRA nuclear@2: #define GL_BGRA 0x80E1 nuclear@2: #endif nuclear@2: nuclear@2: #ifndef GL_ABGR nuclear@2: #define GL_ABGR 0x8000 nuclear@2: #endif nuclear@2: nuclear@2: #ifdef UTK_BIG_ENDIAN nuclear@2: #define PIXFMT GL_ABGR nuclear@2: #else nuclear@2: #define PIXFMT GL_BGRA nuclear@2: #endif nuclear@2: nuclear@2: #define MAX_CHARS 128 nuclear@2: #define MAX_IMG_WIDTH 1024 nuclear@2: #define SIZE_PIXELS(x) ((x) / 64) nuclear@2: nuclear@2: struct Font { nuclear@2: unsigned int tex_id; nuclear@2: float scale; // this compensates if a higher res font is loaded in its stead, with a new CreateFont nuclear@2: float line_adv; // vertical advance to go to the next line nuclear@2: struct { nuclear@2: Vector2 pos, size; // glyph position (from origin) and size in normalized coords [0, 1] nuclear@2: float advance; // advance in normalized coords nuclear@2: Vector2 tc_pos, tc_sz; // tex coord box pos/size nuclear@2: } glyphs[MAX_CHARS]; nuclear@2: }; nuclear@2: nuclear@2: static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz); nuclear@2: static void CleanUp(); nuclear@2: nuclear@2: static FT_Library ft; nuclear@2: static Vector2 text_pos; nuclear@2: static float text_size = 1.0; nuclear@2: static Color text_color; nuclear@2: static Font *act_fnt; nuclear@2: nuclear@2: #if !defined(GLIBC) && !defined(__GLIBC__) nuclear@2: float log2(float x) { nuclear@2: float res = 0.0; nuclear@2: while(x > 1.0) { nuclear@2: x /= 2.0; nuclear@2: res += 1.0; nuclear@2: } nuclear@2: return res; nuclear@2: } nuclear@2: #endif // _MSC_VER nuclear@2: nuclear@2: nuclear@2: static inline int NextPow2(int x) nuclear@2: { nuclear@2: float lg2 = log2((float)x); nuclear@2: return (int)pow(2.0f, (int)ceil(lg2)); nuclear@2: } nuclear@2: nuclear@2: nuclear@2: unsigned int CreateFont(const char *fname, int font_size) nuclear@2: { nuclear@2: if(!ft) { nuclear@2: if(FT_Init_FreeType(&ft) != 0) { nuclear@2: fprintf(stderr, "failed to initialize freetype\n"); nuclear@2: return 0; nuclear@2: } nuclear@2: atexit(CleanUp); nuclear@2: } nuclear@2: nuclear@2: FT_Face face; nuclear@2: if(FT_New_Face(ft, fname, 0, &face) != 0) { nuclear@2: fprintf(stderr, "failed to load font: %s\n", fname); nuclear@2: return 0; nuclear@2: } nuclear@2: nuclear@2: FT_Set_Pixel_Sizes(face, 0, font_size); nuclear@2: nuclear@2: Font *fnt = new Font; nuclear@2: int max_width = MAX_CHARS * SIZE_PIXELS(face->bbox.xMax - face->bbox.xMin); nuclear@2: int foo_xsz = MAX_IMG_WIDTH; nuclear@2: int foo_ysz = SIZE_PIXELS(face->bbox.yMax - face->bbox.yMin) * max_width / foo_xsz; nuclear@2: nuclear@2: int tex_xsz = NextPow2(foo_xsz); nuclear@2: int tex_ysz = NextPow2(foo_ysz); nuclear@2: nuclear@2: unsigned int *img; nuclear@2: img = new unsigned int[tex_xsz * tex_ysz]; nuclear@2: memset(img, 0, tex_xsz * tex_ysz * sizeof *img); nuclear@2: nuclear@2: extern int xsz, ysz; nuclear@2: int vport_xsz = xsz, vport_ysz = ysz; nuclear@2: nuclear@2: int max_glyph_y = 0; nuclear@2: int max_glyph_x = 0; nuclear@2: for(int i=0; iglyph->metrics.width); nuclear@2: int height = SIZE_PIXELS(face->glyph->metrics.height); nuclear@2: nuclear@2: if(height > max_glyph_y) { nuclear@2: max_glyph_y = height; nuclear@2: } nuclear@2: nuclear@2: if(width > max_glyph_x) { nuclear@2: max_glyph_x = width; nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: int gx = 0, gy = 0; nuclear@2: for(int i=0; iglyph; nuclear@2: nuclear@2: int gwidth = SIZE_PIXELS(g->metrics.width); nuclear@2: int gheight = SIZE_PIXELS(g->metrics.height); nuclear@2: nuclear@2: if(gx > MAX_IMG_WIDTH - gwidth) { nuclear@2: gx = 0; nuclear@2: gy += max_glyph_y; nuclear@2: } nuclear@2: nuclear@2: BlitFontGlyph(fnt, gx, gy, g, img, tex_xsz, tex_ysz); nuclear@2: fnt->scale = 1.0; nuclear@2: fnt->line_adv = (float)SIZE_PIXELS(g->metrics.vertAdvance) / (float)vport_ysz; nuclear@2: fnt->glyphs[i].tc_pos.x = (float)gx / (float)tex_xsz; nuclear@2: fnt->glyphs[i].tc_pos.y = (float)gy / (float)tex_ysz; nuclear@2: fnt->glyphs[i].tc_sz.x = (float)gwidth / (float)tex_xsz; nuclear@2: fnt->glyphs[i].tc_sz.y = (float)gheight / (float)tex_ysz; nuclear@2: fnt->glyphs[i].size.x = (float)gwidth / (float)vport_xsz; nuclear@2: fnt->glyphs[i].size.y = (float)gheight / (float)vport_ysz; nuclear@2: fnt->glyphs[i].pos.x = (float)SIZE_PIXELS(g->metrics.horiBearingX) / (float)vport_xsz; nuclear@2: fnt->glyphs[i].pos.y = -(float)SIZE_PIXELS(g->metrics.horiBearingY) / (float)vport_ysz; nuclear@2: fnt->glyphs[i].advance = (float)SIZE_PIXELS(g->metrics.horiAdvance) / (float)vport_xsz; nuclear@2: nuclear@2: gx += gwidth; nuclear@2: } nuclear@2: nuclear@2: FT_Done_Face(face); nuclear@2: nuclear@2: glGenTextures(1, &fnt->tex_id); nuclear@2: glBindTexture(GL_TEXTURE_2D, fnt->tex_id); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); nuclear@2: glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); nuclear@2: glTexImage2D(GL_TEXTURE_2D, 0, 4, tex_xsz, tex_ysz, 0, PIXFMT, GL_UNSIGNED_BYTE, img); nuclear@2: nuclear@2: #ifdef DUMP_FONT_IMG nuclear@2: FILE *fp; nuclear@2: unsigned int *ptr = img; nuclear@2: nuclear@2: if((fp = fopen("fnt.ppm", "wb"))) { nuclear@2: fprintf(fp, "P6\n%d %d\n255\n", tex_xsz, tex_ysz); nuclear@2: nuclear@2: for(int i=0; i> 24) & 0xff, fp); nuclear@2: fputc((*ptr >> 24) & 0xff, fp); nuclear@2: fputc((*ptr >> 24) & 0xff, fp); nuclear@2: ptr++; nuclear@2: } nuclear@2: fclose(fp); nuclear@2: } nuclear@2: #endif nuclear@2: nuclear@2: delete [] img; nuclear@2: nuclear@2: act_fnt = fnt; nuclear@2: nuclear@2: return 1; nuclear@2: } nuclear@2: nuclear@2: void DeleteFont(unsigned int fid) nuclear@2: { nuclear@2: } nuclear@2: nuclear@2: unsigned int GetFont(const char *fname, int sz) nuclear@2: { nuclear@2: return 1; nuclear@2: } nuclear@2: nuclear@2: bool BindFont(unsigned int fid) nuclear@2: { nuclear@2: return true; nuclear@2: } nuclear@2: nuclear@2: void SetTextPos(const Vector2 &pos) nuclear@2: { nuclear@2: text_pos = pos; nuclear@2: } nuclear@2: nuclear@2: Vector2 GetTextPos() nuclear@2: { nuclear@2: return text_pos; nuclear@2: } nuclear@2: nuclear@2: void TextLineAdvance(int adv) nuclear@2: { nuclear@2: text_pos.y += (float)adv * act_fnt->line_adv; nuclear@2: } nuclear@2: nuclear@2: void TextCRet() nuclear@2: { nuclear@2: text_pos.x = 0.0; nuclear@2: } nuclear@2: nuclear@2: void SetTextSize(float sz) nuclear@2: { nuclear@2: text_size = sz; nuclear@2: } nuclear@2: nuclear@2: float GetTextSize() nuclear@2: { nuclear@2: return text_size; nuclear@2: } nuclear@2: nuclear@2: void SetTextColor(const Color &col) nuclear@2: { nuclear@2: text_color = col; nuclear@2: } nuclear@2: nuclear@2: Color GetTextColor() nuclear@2: { nuclear@2: return text_color; nuclear@2: } nuclear@2: nuclear@2: static void ImOverlay(const Vector2 &v1, const Vector2 &v2, const Color &col, unsigned int tex) nuclear@2: { nuclear@2: float l = v1.x * 2.0f - 1.0f; nuclear@2: float r = v2.x * 2.0f - 1.0f; nuclear@2: float u = -v1.y * 2.0f + 1.0f; nuclear@2: float d = -v2.y * 2.0f + 1.0f; nuclear@2: nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glPushMatrix(); nuclear@2: glLoadIdentity(); nuclear@2: glMatrixMode(GL_MODELVIEW); nuclear@2: glPushMatrix(); nuclear@2: glLoadIdentity(); nuclear@2: nuclear@2: glBindTexture(GL_TEXTURE_2D, tex); nuclear@2: nuclear@2: glDisable(GL_DEPTH_TEST); nuclear@2: glDisable(GL_LIGHTING); nuclear@2: glDisable(GL_CULL_FACE); nuclear@2: nuclear@2: glBegin(GL_QUADS); nuclear@2: glColor4f(col.r, col.g, col.b, col.a); nuclear@2: glTexCoord2f(0, 0); nuclear@2: glVertex2f(l, u); nuclear@2: glTexCoord2f(1, 0); nuclear@2: glVertex2f(r, u); nuclear@2: glTexCoord2f(1, 1); nuclear@2: glVertex2f(r, d); nuclear@2: glTexCoord2f(0, 1); nuclear@2: glVertex2f(l, d); nuclear@2: glEnd(); nuclear@2: nuclear@2: glEnable(GL_LIGHTING); nuclear@2: glEnable(GL_DEPTH_TEST); nuclear@2: nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glPopMatrix(); nuclear@2: glMatrixMode(GL_MODELVIEW); nuclear@2: glPopMatrix(); nuclear@2: } nuclear@2: nuclear@2: float PrintChar(char c) nuclear@2: { nuclear@2: // get texture coordinates for the glyph, and construct the texture matrix nuclear@2: float tx = act_fnt->glyphs[(int)c].tc_pos.x; nuclear@2: float ty = act_fnt->glyphs[(int)c].tc_pos.y; nuclear@2: float sx = act_fnt->glyphs[(int)c].tc_sz.x; nuclear@2: float sy = act_fnt->glyphs[(int)c].tc_sz.y; nuclear@2: nuclear@2: float mat[] = { nuclear@2: sx, 0, tx, 0, nuclear@2: 0, sy, ty, 0, nuclear@2: 0, 0, 1, 0, nuclear@2: 0, 0, 0, 1 nuclear@2: }; nuclear@2: nuclear@2: glMatrixMode(GL_TEXTURE); nuclear@2: glPushMatrix(); nuclear@2: glLoadMatrixf(mat); nuclear@2: nuclear@2: Vector2 pos = text_pos + act_fnt->glyphs[(int)c].pos * act_fnt->scale; nuclear@2: ImOverlay(pos, pos + act_fnt->glyphs[(int)c].size * act_fnt->scale, text_color, act_fnt->tex_id); nuclear@2: nuclear@2: glMatrixMode(GL_TEXTURE); nuclear@2: glPopMatrix(); nuclear@2: nuclear@2: return act_fnt->glyphs[(int)c].advance * act_fnt->scale; nuclear@2: } nuclear@2: nuclear@2: // this function contains the preamble of all block text drawing functions (i.e. not printchar above) nuclear@2: static void PreDraw() nuclear@2: { nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glPushMatrix(); nuclear@2: /*glLoadTransposeMatrixf(OrthoProj(2, 2, 0, 10).m);*/ nuclear@2: glLoadIdentity(); nuclear@2: glMatrixMode(GL_MODELVIEW); nuclear@2: glPushMatrix(); nuclear@2: glLoadIdentity(); nuclear@2: glMatrixMode(GL_TEXTURE); nuclear@2: glPushMatrix(); nuclear@2: glLoadIdentity(); nuclear@2: nuclear@2: glEnable(GL_TEXTURE_2D); nuclear@2: glBindTexture(GL_TEXTURE_2D, act_fnt->tex_id); nuclear@2: nuclear@2: glBegin(GL_QUADS); nuclear@2: glColor4f(text_color.r, text_color.g, text_color.b, text_color.a); nuclear@2: } nuclear@2: nuclear@2: static void PostDraw() nuclear@2: { nuclear@2: glEnd(); nuclear@2: nuclear@2: glDisable(GL_TEXTURE_2D); nuclear@2: nuclear@2: glMatrixMode(GL_TEXTURE); nuclear@2: glPopMatrix(); nuclear@2: glMatrixMode(GL_MODELVIEW); nuclear@2: glPopMatrix(); nuclear@2: glMatrixMode(GL_PROJECTION); nuclear@2: glPopMatrix(); nuclear@2: } nuclear@2: nuclear@2: float PrintString(const char *str, bool standalone) nuclear@2: { nuclear@2: if(standalone) PreDraw(); nuclear@2: nuclear@2: float start_x = text_pos.x; nuclear@2: while(*str) { nuclear@2: float tx = act_fnt->glyphs[(int)*str].tc_pos.x; nuclear@2: float ty = act_fnt->glyphs[(int)*str].tc_pos.y; nuclear@2: float sx = act_fnt->glyphs[(int)*str].tc_sz.x; nuclear@2: float sy = act_fnt->glyphs[(int)*str].tc_sz.y; nuclear@2: nuclear@2: Vector2 tc1 = Vector2(tx, ty); nuclear@2: Vector2 tc2 = Vector2(tx + sx, ty + sy); nuclear@2: nuclear@2: Vector2 v1 = text_pos + act_fnt->glyphs[(int)*str].pos * act_fnt->scale * text_size; nuclear@2: Vector2 v2 = v1 + act_fnt->glyphs[(int)*str].size * act_fnt->scale * text_size; nuclear@2: float l = v1.x * 2.0f - 1.0f; nuclear@2: float r = v2.x * 2.0f - 1.0f; nuclear@2: float u = -v1.y * 2.0f + 1.0f; nuclear@2: float d = -v2.y * 2.0f + 1.0f; nuclear@2: nuclear@2: glTexCoord2f(tc1.x, tc1.y); nuclear@2: glVertex2f(l, u); nuclear@2: glTexCoord2f(tc2.x, tc1.y); nuclear@2: glVertex2f(r, u); nuclear@2: glTexCoord2f(tc2.x, tc2.y); nuclear@2: glVertex2f(r, d); nuclear@2: glTexCoord2f(tc1.x, tc2.y); nuclear@2: glVertex2f(l, d); nuclear@2: nuclear@2: text_pos.x += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size; nuclear@2: } nuclear@2: nuclear@2: if(standalone) PostDraw(); nuclear@2: return text_pos.x - start_x; nuclear@2: } nuclear@2: nuclear@2: void PrintStringLines(const char **str, int lines) nuclear@2: { nuclear@2: PreDraw(); nuclear@2: nuclear@2: while(lines-- > 0) { nuclear@2: PrintString(*str++, false); nuclear@2: TextLineAdvance(); nuclear@2: TextCRet(); nuclear@2: } nuclear@2: nuclear@2: PostDraw(); nuclear@2: } nuclear@2: nuclear@2: static void BlitFontGlyph(Font *fnt, int x, int y, FT_GlyphSlot glyph, unsigned int *img, int xsz, int ysz) nuclear@2: { nuclear@2: if(glyph->bitmap.pixel_mode != FT_PIXEL_MODE_GRAY) { nuclear@2: fprintf(stderr, "BlitFontGlyph: unsupported pixel mode: %d\n", glyph->bitmap.pixel_mode); nuclear@2: return; nuclear@2: } nuclear@2: nuclear@2: unsigned int *dest = img + y * xsz + x; nuclear@2: unsigned char *src = glyph->bitmap.buffer; nuclear@2: nuclear@2: for(int j=0; jbitmap.rows; j++) { nuclear@2: for(int i=0; ibitmap.width; i++) { nuclear@2: dest[i] = 0x00ffffff | ((unsigned int)src[i] << 24); nuclear@2: } nuclear@2: dest += xsz; nuclear@2: src += glyph->bitmap.pitch; nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: static void CleanUp() nuclear@2: { nuclear@2: FT_Done_FreeType(ft); nuclear@2: } nuclear@2: nuclear@2: float GetMaxDescent() nuclear@2: { nuclear@2: Font *fnt = act_fnt; nuclear@2: nuclear@2: float max_descent = 0.0f; nuclear@2: nuclear@2: for(int i=0; iglyphs[i].pos.y + fnt->glyphs[i].size.y; nuclear@2: if(isprint(i) && des > max_descent) { nuclear@2: max_descent = des; nuclear@2: } nuclear@2: } nuclear@2: nuclear@2: return max_descent; nuclear@2: } nuclear@2: nuclear@2: float GetLineAdvance() nuclear@2: { nuclear@2: return act_fnt->line_adv; nuclear@2: } nuclear@2: nuclear@2: float GetTextWidth(const char *str) nuclear@2: { nuclear@2: float width = 0; nuclear@2: while(*str) { nuclear@2: width += act_fnt->glyphs[(int)*str++].advance * act_fnt->scale * text_size; nuclear@2: } nuclear@2: return width; nuclear@2: }