From c3db67165666d6e978faee1b199de2912cb1eb34 Mon Sep 17 00:00:00 2001 From: Nick Gasson Date: Sat, 24 Oct 2009 22:16:44 +0100 Subject: [PATCH] Nice new font class --- CMakeLists.txt | 2 + src/UIDemo.cpp | 7 +- src/ft/Font.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++++ src/ft/IFont.hpp | 37 +++++++ src/gui/Font.cpp | 6 +- src/gui2/Theme.cpp | 2 +- 6 files changed, 290 insertions(+), 5 deletions(-) create mode 100644 src/ft/Font.cpp create mode 100644 src/ft/IFont.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ee4a791..ae2c9ba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ set (CMAKE_BUILD_TYPE RelWithDebInfo) # Find all the source files file (GLOB folder_source ${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/src/ft/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gui/*.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/gui2/*.cpp) source_group (${PROJECT_NAME} FILES ${folder_source}) @@ -35,6 +36,7 @@ find_package (Xerces REQUIRED) find_package (FLTK REQUIRED) include_directories (${CMAKE_SOURCE_DIR}/include) +include_directories (${CMAKE_SOURCE_DIR}/src) include_directories (include ${SDL_INCLUDE_DIR}) include_directories (include ${SDL_IMAGE_INCLUDE_DIR}) include_directories (include ${OPENGL_INCLUDE_DIR}) diff --git a/src/UIDemo.cpp b/src/UIDemo.cpp index 81447c8..1bfa268 100644 --- a/src/UIDemo.cpp +++ b/src/UIDemo.cpp @@ -17,6 +17,7 @@ #include "IScreen.hpp" #include "gui2/ILayout.hpp" +#include "ft/IFont.hpp" class UIDemo : public IScreen { public: @@ -37,16 +38,20 @@ public: private: gui::ILayoutPtr layout; + ft::IFontPtr font; }; UIDemo::UIDemo() { layout = gui::make_layout("layouts/demo.xml"); + font = ft::load_font("data/fonts/Vera.ttf", 16); } void UIDemo::overlay() const { - layout->render(); + //layout->render(); + + font->print(100, 100, "yah"); } IScreenPtr make_ui_demo() diff --git a/src/ft/Font.cpp b/src/ft/Font.cpp new file mode 100644 index 0000000..89f1de3 --- /dev/null +++ b/src/ft/Font.cpp @@ -0,0 +1,241 @@ +// +// Copyright (C) 2009 Nick Gasson +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#include "ft/IFont.hpp" +#include "ILogger.hpp" + +#include +#include +#include +#include + +#include + +#include + +#include "ft2build.h" +#include FT_FREETYPE_H +#include FT_GLYPH_H + +using namespace ft; + +class Glyph { +public: + Glyph(FT_Face& face, FT_ULong uch); + ~Glyph(); + + void render() const; + + float width() const { return width_; } + float height() const { return height_; } + + float advance_x() const { return advance_x_; } + float advance_y() const { return advance_y_; } +private: + static int next_power_of_2(int a); + + GLuint tex; + float width_, height_; + float tex_xmax, tex_ymax; + float top, left; + float advance_x_, advance_y_; +}; + +Glyph::Glyph(FT_Face& face, FT_ULong uch) +{ + int index = FT_Get_Char_Index(face, uch); + + int err = FT_Load_Glyph(face, index, FT_LOAD_DEFAULT); + if (err) + throw runtime_error("FT_Load_Glyph failed for char code " + + boost::lexical_cast(uch)); + + err = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + assert(err == 0); + + glGenTextures(1, &tex); + + glBindTexture(GL_TEXTURE_2D, tex); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + FT_Bitmap& bmp = face->glyph->bitmap; + + // Pad out the bitmap to be a power-of-2 square + + int tex_width = next_power_of_2(bmp.width); + int tex_height = next_power_of_2(bmp.rows); + + GLubyte* expanded = new GLubyte[2 * tex_width * tex_height]; + + for (int j = 0; j < tex_height; j++) { + for (int i = 0; i < tex_width; i++) { + expanded[2*(i + j*tex_width)] + = expanded[2*(i + j*tex_width) + 1] + = (i >= bmp.width || j >= bmp.rows) + ? 0 + : bmp.buffer[i + bmp.width*j]; + } + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_width, + tex_height, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, expanded); + + delete[] expanded; + + width_ = static_cast(bmp.width); + height_ = static_cast(bmp.rows); + + tex_xmax = width_ / tex_width; + tex_ymax = height_ / tex_height; + + top = face->glyph->bitmap_top; + left = face->glyph->bitmap_left; + + advance_x_ = face->glyph->advance.x >> 6; + advance_y_ = face->glyph->advance.y >> 6; +} + +Glyph::~Glyph() +{ + glDeleteTextures(1, &tex); +} + +void Glyph::render() const +{ + glBindTexture(GL_TEXTURE_2D, tex); + + glBegin(GL_QUADS); + + glTexCoord2f(0, tex_ymax); + glVertex2f(left, height_ - top); + + glTexCoord2f(0, 0); + glVertex2f(left, -top); + + glTexCoord2f(tex_xmax, 0); + glVertex2f(left + width_, -top); + + glTexCoord2f(tex_xmax, tex_ymax); + glVertex2f(left + width_, height_ - top); + + glEnd(); + + glTranslatef(advance_x_, advance_y_, 0.0f); +} + +int Glyph::next_power_of_2(int a) +{ + int rval = 1; + while (rval < a) + rval <<= 1; + + return rval; +} + +namespace {// REMOVE + +class Font : public IFont { +public: + Font(const string& file, int h); + ~Font(); + + void print(int x, int y, const string& s) const; +private: + FT_Face face; + vector glyphs; + + static FT_Library library; + static int library_ref_count; +}; + +FT_Library Font::library; +int Font::library_ref_count = 0; + +Font::Font(const string& file, int h) +{ + if (++library_ref_count == 1) { + if (FT_Init_FreeType(&library)) + throw runtime_error("FT_Init_FreeType failed"); + } + + int err = FT_New_Face(library, file.c_str(), 0, &face); + if (err == FT_Err_Unknown_File_Format) + throw runtime_error("Unsupported font file format: " + file); + else if (err) + throw runtime_error("Failed to load font: " + file); + + err = FT_Set_Char_Size(face, + 0, // Width in 1/64th of point + h*64, // Height in 1/64th of point + 0, 0); // Default DPI + + for (char ch = 0; ch < 127; ch++) + glyphs.push_back(new Glyph(face, ch)); + + log() << "Loaded font " << file; +} + +Font::~Font() +{ + for (vector::iterator it = glyphs.begin(); + it != glyphs.end(); ++it) + delete *it; + + if (--library_ref_count == 0) + FT_Done_FreeType(library); +} + +void Font::print(int x, int y, const string& s) const +{ + glPushAttrib(GL_ENABLE_BIT); + + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + + glPushMatrix(); + + glColor3f(0.0f, 0.0f, 0.0f); + glTranslatef(x, y, 0.0f); + + for (string::const_iterator it = s.begin(); + it != s.end(); ++it) + glyphs.at(*it)->render(); + + glPopMatrix(); + glPopAttrib(); +} + +} +IFontPtr ft::load_font(const string& file, int h) +{ + typedef tuple FontToken; + static map cache; + + FontToken t = make_tuple(file, h); + map::iterator it = cache.find(t); + + if (it != cache.end()) + return (*it).second; + else { + IFontPtr p(new Font(file, h)); + cache[t] = p; + return p; + } +} + + diff --git a/src/ft/IFont.hpp b/src/ft/IFont.hpp new file mode 100644 index 0000000..2ba61f6 --- /dev/null +++ b/src/ft/IFont.hpp @@ -0,0 +1,37 @@ +// +// Copyright (C) 2009 Nick Gasson +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +#ifndef INC_FT_IFONT_HPP +#define INC_FT_IFONT_HPP + +#include "Platform.hpp" + +namespace ft { + + struct IFont { + virtual ~IFont() {} + + virtual void print(int x, int y, const string& s) const = 0; + }; + + typedef shared_ptr IFontPtr; + + IFontPtr load_font(const string& file, int h); + +} + +#endif diff --git a/src/gui/Font.cpp b/src/gui/Font.cpp index 2f3abc5..1751803 100644 --- a/src/gui/Font.cpp +++ b/src/gui/Font.cpp @@ -96,7 +96,7 @@ Font::Font(const string& aFile, int aHeight, bool shadow) throw runtime_error("FT_New_Face failed, file name: " + aFile); // FreeType measures font sizes in 1/64ths of a pixel... - FT_Set_Char_Size(face, aHeight<<6, aHeight<<6, 96, 96); + FT_Set_Char_Size(face, aHeight<<6, aHeight<<6, 0, 0); listBase = glGenLists(128); glGenTextures(128, textures); @@ -162,7 +162,7 @@ void Font::makeDisplayList(FT_Face face, char ch, GLuint listBase, 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_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_MONO, 0, 1); FT_BitmapGlyph bitmapGlyph = (FT_BitmapGlyph)glyph; // Get a reference to the bitmap @@ -196,7 +196,7 @@ void Font::makeDisplayList(FT_Face face, char ch, GLuint listBase, // Free expanded data delete[] expandedData; - + // Create the display list glNewList(listBase+ch, GL_COMPILE); glBindTexture(GL_TEXTURE_2D, texBase[(int)ch]); diff --git a/src/gui2/Theme.cpp b/src/gui2/Theme.cpp index 9719c18..39d1b63 100644 --- a/src/gui2/Theme.cpp +++ b/src/gui2/Theme.cpp @@ -21,7 +21,7 @@ using namespace gui; Theme::Theme() { - normal_font_ = load_font("data/fonts/Vera.ttf", 11, false); + normal_font_ = load_font("data/fonts/DejaVuSansMono.ttf", 15, false); } Colour Theme::background() const -- 2.39.2