From 23be79caaf2e3f0002f49d3b2be9d4073b9f6f37 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 7 Jun 2008 20:35:17 +0100 Subject: [PATCH] Start factoring FreeType into Font class --- src/FreeType.cpp | 231 ++++++++++++++++++++++++++++++++++++++++++++++- src/FreeType.hpp | 21 +++++ src/Menu.cpp | 9 +- src/Menu.hpp | 2 + 4 files changed, 259 insertions(+), 4 deletions(-) diff --git a/src/FreeType.cpp b/src/FreeType.cpp index 6e2524c..cf071d8 100644 --- a/src/FreeType.cpp +++ b/src/FreeType.cpp @@ -17,8 +17,235 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "FreeType.hpp" -#include "OpenGL.hpp" +#include "FreeType.hpp" + +#include "OpenGL.hpp" // TODO: Remove + +int Font::fontRefCount = 0; +FT_Library Font::library; + +Font::Font(string filename, unsigned int h) +{ + if (++fontRefCount == 1) { + if (FT_Init_FreeType(&library)) + throw runtime_error("FT_Init_FreeType failed"); + + cout << "Loaded freetype library" << endl; + } + + unsigned char i; + + // Allocate memory for textures + textures = new GLuint[128]; + height = (float)h; + widths = new unsigned short[128]; + + // Create the face + FT_Face face; + if (FT_New_Face(library, filename.c_str(), 0, &face)) + throw runtime_error("FT_New_Face failed, file name: " + filename); + + // FreeType measures font sizes in 1/64ths of a pixel... + FT_Set_Char_Size(face, h<<6, h<<6, 96, 96); + + listBase = glGenLists(128); + glGenTextures(128, textures); + + // Generate the characters + for (i = 0; i < 128; i++) + MakeDisplayList(face, i, listBase, textures, widths); + + // Free face data + FT_Done_Face(face); +} + +Font::~Font() +{ + glDeleteLists(listBase, 128); + glDeleteTextures(128, textures); + delete[] textures; + delete[] widths; + + if (--fontRefCount == 0) { + FT_Done_FreeType(library); + cout << "Unloaded freetype library" << endl; + } +} + +int Font::NextPowerOf2(int a) +{ + int rval = 1; + + while (rval < a) + rval <<= 1; + + return rval; +} + +void Font::MakeDisplayList(FT_Face face, char ch, GLuint listBase, + GLuint *texBase, unsigned short *widths) +{ + int i, j; + + // Load the character's glyph + if (FT_Load_Glyph(face, FT_Get_Char_Index(face, ch), FT_LOAD_DEFAULT)) + throw runtime_error("FT_Load_Glyph failed"); + + // Store the face's glyph in a glyph object + FT_Glyph glyph; + if (FT_Get_Glyph(face->glyph, &glyph)) + throw runtime_error("FT_Get_Glyph failed"); + + // Convert the glyph to a bitmap + FT_Glyph_To_Bitmap(&glyph, ft_render_mode_normal, 0, 1); + FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; + + // Get a reference to the bitmap + FT_Bitmap& bitmap = bitmapGlyph->bitmap; + + // Make the width and height a power of 2 + int width = NextPowerOf2(bitmap.width); + int height = NextPowerOf2(bitmap.rows); + + // Allocate memory for the texture data + GLubyte* expandedData = new GLubyte[2 * width * height]; + + // Fill in the bitmap's extended data + for (j = 0; j < height; j++) { + for (i = 0; i < width; i++) { + expandedData[2*(i+j*width)] = expandedData[2*(i+j*width)+1] = + (i >= bitmap.width || j >= bitmap.rows) ? 0 : bitmap.buffer[i + bitmap.width*j]; + } + } + + // Set texture parameters + glBindTexture(GL_TEXTURE_2D, texBase[ch]); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + // Create the texture + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, + GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expandedData); + + // Free expanded data + delete[] expandedData; + + // Create the display list + glNewList(listBase+ch, GL_COMPILE); + glBindTexture(GL_TEXTURE_2D, texBase[ch]); + + glPushMatrix(); + + // Insert some space between characters + glTranslatef((float)bitmapGlyph->left, 0, 0); + widths[ch] = bitmapGlyph->left; + + // Move down a bit to accomodate characters such as p and q + glTranslatef(0, (float)(-bitmapGlyph->top), 0); + + float x = (float)bitmap.width / (float)width; + float y = (float)bitmap.rows / (float)height; + + // Draw the quad + glBegin(GL_QUADS); + glTexCoord2f(0, y); glVertex2f(0, (float)bitmap.rows); + glTexCoord2f(0, 0); glVertex2f(0, 0); + glTexCoord2f(x, 0); glVertex2f((float)bitmap.width, 0); + glTexCoord2f(x, y); glVertex2f((float)bitmap.width, (float)bitmap.rows); + glEnd(); + + glPopMatrix(); + + // Move along to the next character + glTranslatef((float)(face->glyph->advance.x >> 6), 0, 0); + widths[ch] += (short)face->glyph->advance.x >> 6; + + // Finish display list + glEndList(); +} + +void Font::Print(int x, int y, const char *fmt, ...) +{ + vector lines; + + // Store the current matrix + glPushMatrix(); + + GLuint font = listBase; + float h = height / 0.63f; // Add some space between lines + const int MAX_TEXT_LEN = 256; + char text[MAX_TEXT_LEN]; + va_list ap; + + if (fmt == NULL) + *text=0; + else { + va_start(ap, fmt); + vsnprintf(text, MAX_TEXT_LEN, fmt, ap); + va_end(ap); + } + + // Split text into lines - based on NeHe code + const char *start_line = text, *c; + for (c = text; *c; c++) { + if (*c == '\n') { + string line; + for (const char *n = start_line; n < c; n++) + line.append(1, *n); + lines.push_back(line); + start_line = c+1; + } + } + + if (start_line) { + string line; + for (const char *n = start_line; n < c; n++) + line.append(1, *n); + lines.push_back(line); + } + + // Set attributes + glEnable(GL_TEXTURE_2D); + glDisable(GL_DEPTH_TEST); + glEnable(GL_BLEND); + glListBase(font); + + // Draw the text + for (unsigned int i = 0; i < lines.size(); i++) { + glPushMatrix(); + glLoadIdentity(); + glTranslatef((float)x, (float)y - h*i, 0.0f); + + glCallLists((GLsizei)lines[i].length(), GL_UNSIGNED_BYTE, lines[i].c_str()); + + glPopMatrix(); + } + + // Restore previous matrix + glPopMatrix(); + glPopMatrix(); +} + +int Font::GetStringWidth(const char *s) +{ + size_t i = strlen(s); + int result = 0; + while (i--) + result += widths[s[i]]; + + return result; +} + + + + + + + + + + + FreeType::FreeType() { diff --git a/src/FreeType.hpp b/src/FreeType.hpp index 28fd196..5071363 100644 --- a/src/FreeType.hpp +++ b/src/FreeType.hpp @@ -22,6 +22,27 @@ #include "Platform.hpp" +class Font { +public: + Font(string filename, unsigned int h); + ~Font(); + + void Print(int x, int y, const char *fmt, ...); + int GetStringWidth(const char *s); +private: + int NextPowerOf2(int a); + void MakeDisplayList(FT_Face face, char ch, GLuint listBase, + GLuint *texBase, unsigned short *widths); + + GLuint *textures; + GLuint listBase; + float height; + unsigned short *widths; + + static int fontRefCount; + static FT_Library library; +}; + /* * Wrapper around the GNU FreeType font library. Code adapted from NeHe. * tutorial #43. diff --git a/src/Menu.cpp b/src/Menu.cpp index 248448e..53f3d92 100644 --- a/src/Menu.cpp +++ b/src/Menu.cpp @@ -43,7 +43,8 @@ MainMenu::MainMenu() scoreOpt("images/score_option.png", OPTIONS_OFFSET, 1), optionsOpt("images/options_option.png", OPTIONS_OFFSET, 2), exitOpt("images/exit_option.png", OPTIONS_OFFSET, 3), - titleImage("images/title.png") + titleImage("images/title.png"), + hintFont(LocateResource("Default_Font.ttf"), 11) { } @@ -251,6 +252,10 @@ void MainMenu::Display() else hint_timeout--; + glColor4d(0.0, 1.0, 0.0, fade); + hintFont.Print(0, opengl.GetHeight() - 120, hints[hintidx][0]); + + /* opengl.Colour(0.0f, 1.0f, 0.0f, fade); ft.Print(ftNormal, (opengl.GetWidth() - ft.GetStringWidth(ftNormal, hints[hintidx][0])) / 2, @@ -259,7 +264,7 @@ void MainMenu::Display() ft.Print(ftNormal, (opengl.GetWidth() - ft.GetStringWidth(ftNormal, hints[hintidx][1])) / 2, opengl.GetHeight() - 100, - hints[hintidx][1]); + hints[hintidx][1]);*/ } double MenuStar::starRotate = 0.0; diff --git a/src/Menu.hpp b/src/Menu.hpp index eaccbab..298cd0d 100644 --- a/src/Menu.hpp +++ b/src/Menu.hpp @@ -23,6 +23,7 @@ #include "ScreenManager.hpp" #include "Mechanics.hpp" #include "Image.hpp" +#include "FreeType.hpp" class MenuStar { @@ -76,6 +77,7 @@ private: double fade, bigness; MenuState state; Image titleImage; + Font hintFont; int hint_timeout, hintidx; -- 2.39.2