From 9bb913a9b13917818492b5f4ee61e49d1de3ae3a Mon Sep 17 00:00:00 2001 From: cozman Date: Mon, 18 Jul 2005 22:19:16 +0000 Subject: [PATCH] Initial revision --- GLFT_Font.cpp | 363 ++++++++++++++++++++++++++++++++++++++++++++++++++ GLFT_Font.hpp | 112 ++++++++++++++++ README | 34 +++++ index.html | 55 ++++++++ test.cpp | 53 ++++++++ 5 files changed, 617 insertions(+) create mode 100644 GLFT_Font.cpp create mode 100644 GLFT_Font.hpp create mode 100644 README create mode 100644 index.html create mode 100644 test.cpp diff --git a/GLFT_Font.cpp b/GLFT_Font.cpp new file mode 100644 index 0000000..be80142 --- /dev/null +++ b/GLFT_Font.cpp @@ -0,0 +1,363 @@ +// GLFT_Font (http://www.cs.rit.edu/~jpt2433/glftfont/) +// by James Turk (jpt2433@rit.edu) +// Based on work by Marijn Haverbeke (http://marijn.haverbeke.nl) +// +// Version 0.1 - Released 1 July 2005 - Initial Release +// Version 0.2 - Released 18 July 2005 - Added beginDraw/endDraw, +// Changed vsprintf to vsnprintf +// +// $Id: GLFT_Font.cpp,v 1.1 2005/07/18 22:19:16 cozman Exp $ +// +// Copyright (c) 2005, James Turk +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the GLFT_Font nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + + +#include "GLFT_Font.hpp" + +// static members +FT_Library FTLibraryContainer::library_; +FTLibraryContainer GLFT_Font::library_; + +// FTLibraryContainer implementation // + +FTLibraryContainer::FTLibraryContainer() +{ + if (FT_Init_FreeType(&library_) != 0) + { + throw std::runtime_error("Could not initialize FreeType2 library."); + } +} + +FTLibraryContainer::~FTLibraryContainer() +{ + FT_Done_FreeType(library_); +} + +FT_Library& FTLibraryContainer::getLibrary() +{ + return library_; +} + +std::ostream& operator<<(std::ostream& os, const StreamFlusher& rhs) +{ + return os.flush(); +} + +// GLFT_Font implementation // + +GLFT_Font::GLFT_Font() : + texID_(0), listBase_(0), // initalize GL variables to zero + widths_(NUM_CHARS), // make room for 96 widths + height_(0), drawX_(0), drawY_(0) +{ +} + +GLFT_Font::GLFT_Font(const std::string& filename, unsigned int size) : + texID_(0), listBase_(0), // initalize GL variables to zero + widths_(NUM_CHARS), // make room for 96 widths + height_(0), drawX_(0), drawY_(0) +{ + open(filename, size); +} + +GLFT_Font::~GLFT_Font() +{ + release(); +} + +void GLFT_Font::open(const std::string& filename, unsigned int size) +{ + const size_t MARGIN = 3; + + // release the font if it already exists + if(isValid()) + { + release(); + } + + // Step 1: Open the font using FreeType // + FT_Face face; + + if(FT_New_Face(library_.getLibrary(), filename.c_str(), 0, &face) != 0) + { + throw std::runtime_error("Could not load font file."); + } + + // Abort if this is not a scalable font. + if(!(face->face_flags & FT_FACE_FLAG_SCALABLE) || + !(face->face_flags & FT_FACE_FLAG_HORIZONTAL)) + { + throw std::runtime_error("Invalid font: Error setting font size."); + } + + // Set the font size + FT_Set_Pixel_Sizes(face, size, 0); + + // Step 2: Find maxAscent/Descent to calculate imageHeight // + size_t imageHeight = 0; + size_t imageWidth = 256; + int maxDescent = 0; + int maxAscent = 0; + size_t lineSpace = imageWidth - MARGIN; + size_t lines = 1; + size_t charIndex; + + for(unsigned int ch = 0; ch < NUM_CHARS; ++ch) + { + // Look up the character in the font file. + charIndex = FT_Get_Char_Index(face, ch+SPACE); + + // Render the current glyph. + FT_Load_Glyph(face, charIndex, FT_LOAD_RENDER); + + widths_[ch] = (face->glyph->metrics.horiAdvance >> 6) + MARGIN; + // If the line is full go to the next line + if(widths_[ch] > lineSpace) + { + lineSpace = imageWidth - MARGIN; + ++lines; + } + lineSpace -= widths_[ch]; + + maxAscent = std::max(face->glyph->bitmap_top, maxAscent); + maxDescent = std::max(face->glyph->bitmap.rows - + face->glyph->bitmap_top, maxDescent); + } + + height_ = maxAscent + maxDescent; // calculate height_ for text + + // Compute how high the texture has to be. + size_t neededHeight = (maxAscent + maxDescent + MARGIN) * lines + MARGIN; + // Get the first power of two in which it will fit + imageHeight = 16; + while(imageHeight < neededHeight) + { + imageHeight <<= 1; + } + + // Step 3: Generation of the actual texture // + + // create and zero the memory + unsigned char* image = new unsigned char[imageHeight * imageWidth]; + std::memset(image, 0, imageHeight * imageWidth); + + // These are the position at which to draw the next glyph + size_t x = MARGIN; + size_t y = MARGIN + maxAscent; + float texX1, texX2, texY1, texY2; // used for display list + + listBase_ = glGenLists(NUM_CHARS); // generate the lists for filling + + // Drawing loop + for(unsigned int ch = 0; ch < NUM_CHARS; ++ch) + { + size_t charIndex = FT_Get_Char_Index(face, ch+SPACE); + + // Render the glyph + FT_Load_Glyph(face, charIndex, FT_LOAD_DEFAULT); + FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + + // See whether the character fits on the current line + if(widths_[ch] > imageWidth - x) + { + x = MARGIN; + y += (maxAscent + maxDescent + MARGIN); + } + + // calculate texture coordinates of the character + texX1 = static_cast(x) / imageWidth; + texX2 = static_cast(x+widths_[ch]) / imageWidth; + texY1 = static_cast(y - maxAscent) / imageHeight; + texY2 = texY1 + static_cast(height_) / imageHeight; + + // generate the character's display list + glNewList(listBase_ + ch, GL_COMPILE); + glBegin(GL_QUADS); + glTexCoord2f(texX1,texY1); glVertex2i(0,0); + glTexCoord2f(texX2,texY1); glVertex2i(widths_[ch],0); + glTexCoord2f(texX2,texY2); glVertex2i(widths_[ch],height_); + glTexCoord2f(texX1,texY2); glVertex2i(0,height_); + glEnd(); + glTranslatef(widths_[ch],0,0); // translate forward + glEndList(); + + // copy image generated by FreeType to the texture + for(int row = 0; row < face->glyph->bitmap.rows; ++row) + { + for(int pixel = 0; pixel < face->glyph->bitmap.width; ++pixel) + { + // set pixel at position to intensity (0-255) at the position + image[(x + face->glyph->bitmap_left + pixel) + + (y - face->glyph->bitmap_top + row) * imageWidth] = + face->glyph->bitmap.buffer[pixel + + row * face->glyph->bitmap.pitch]; + } + } + + x += widths_[ch]; + } + + // generate the OpenGL texture from the byte array + glGenTextures(1, &texID_); + glBindTexture(GL_TEXTURE_2D, texID_); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, imageWidth, imageHeight, 0, + GL_ALPHA, GL_UNSIGNED_BYTE, image); + + delete[] image; // now done with the image memory + FT_Done_Face(face); // free the face data +} + +void GLFT_Font::release() +{ + if(glIsList(listBase_)) + { + glDeleteLists(listBase_, NUM_CHARS); + } + if(glIsTexture(texID_)) + { + glDeleteTextures(1, &texID_); + } + + // clear out data + texID_ = 0; + listBase_ = 0; + widths_.clear(); + widths_.resize(NUM_CHARS); + height_ = 0; +} + +bool GLFT_Font::isValid() const +{ + return glIsTexture(texID_) == GL_TRUE; +} + +void GLFT_Font::drawText(float x, float y, const char *str, ...) const +{ + if(!isValid()) + { + throw std::logic_error("Invalid GLFT_Font::drawText call."); + } + + std::va_list args; + char buf[1024]; + + va_start(args,str); + std::vsnprintf(buf, 1024, str, args); // avoid buffer overflow + va_end(args); + + glBindTexture(GL_TEXTURE_2D, texID_); + glPushMatrix(); + glTranslated(x,y,0); + for(unsigned int i=0; i < std::strlen(buf); ++i) + { + unsigned char ch( buf[i] - SPACE ); // ch-SPACE = DisplayList offset + // replace characters outside the valid range with undrawable + if(ch > NUM_CHARS) + { + ch = NUM_CHARS-1; // last character is 'undrawable' + } + glCallList(listBase_+ch); // calculate list to call + } + + // Alternative, ignores undrawables (no noticable speed difference) + //glListBase(listBase_-32); + //glCallLists(static_cast(std::strlen(buf)), GL_UNSIGNED_BYTE, buf); + + glPopMatrix(); +} + +void GLFT_Font::drawText(float x, float y, const std::string& str) const +{ + if(!isValid()) + { + throw std::logic_error("Invalid GLFT_Font::drawText call."); + } + + glBindTexture(GL_TEXTURE_2D, texID_); + glPushMatrix(); + glTranslated(x,y,0); + for(std::string::const_iterator i = str.begin(); i != str.end(); ++i) + { + unsigned char ch( *i - SPACE ); // ch-SPACE = DisplayList offset + // replace characters outside the valid range with undrawable + if(ch > NUM_CHARS) + { + ch = NUM_CHARS-1; // last character is 'undrawable' + } + glCallList(listBase_+ch); // calculate list to call + } + + // Alternative, ignores undrawables (no noticable speed difference) + //glListBase(listBase_-32); + //glCallLists(static_cast(std::strlen(buf)), GL_UNSIGNED_BYTE, buf); + + glPopMatrix(); +} + +std::ostream& GLFT_Font::beginDraw(float x, float y) +{ + // clear the string and store the draw-position + ss_.str(""); + drawX_ = x; + drawY_ = y; + return ss_; +} + +StreamFlusher GLFT_Font::endDraw() +{ + drawText(drawX_, drawY_, ss_.str()); // draw the string + ss_.str(""); // clear the buffer + return StreamFlusher(); +} + +unsigned int GLFT_Font::calcStringWidth(const std::string& str) const +{ + if(!isValid()) + { + throw std::logic_error("Invalid GLFT_Font::calcStringWidth call."); + } + unsigned int width=0; + + // iterate through widths of each char and accumulate width of string + for(std::string::const_iterator i = str.begin(); i < str.end(); ++i) + { + width += widths_[static_cast(*i) - SPACE]; + } + + return width; +} + +unsigned int GLFT_Font::getHeight() const +{ + if(!isValid()) + { + throw std::logic_error("Invalid GLFT_Font::getHeight call."); + } + return height_; +} diff --git a/GLFT_Font.hpp b/GLFT_Font.hpp new file mode 100644 index 0000000..208c554 --- /dev/null +++ b/GLFT_Font.hpp @@ -0,0 +1,112 @@ +// GLFT_Font (http://www.cs.rit.edu/~jpt2433/glftfont/) +// by James Turk (jpt2433@rit.edu) +// Based on work by Marijn Haverbeke (http://marijn.haverbeke.nl) +// +// Version 0.1 - Released 1 July 2005 - Initial Release +// Version 0.2 - Released 18 July 2005 - Added beginDraw/endDraw, +// Changed vsprintf to vsnprintf +// +// $Id: GLFT_Font.hpp,v 1.1 2005/07/18 22:19:16 cozman Exp $ +// +// Copyright (c) 2005, James Turk +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the GLFT_Font nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + + +#ifndef GLFT_FONT_HPP +#define GLFT_FONT_HPP + +#include + +#include +#include FT_FREETYPE_H + +#include +#include +#include +#include +#include + +class FTLibraryContainer +{ +public: + FTLibraryContainer(); + ~FTLibraryContainer(); + + FT_Library& getLibrary(); + +private: + static FT_Library library_; +}; + +class StreamFlusher { }; +std::ostream& operator<<(std::ostream& os, const StreamFlusher& rhs); + +class GLFT_Font +{ +public: + GLFT_Font(); + GLFT_Font(const std::string& filename, unsigned int size); + + ~GLFT_Font(); + + void open(const std::string& filename, unsigned int size); + void release(); + + bool isValid() const; + + void drawText(float x, float y, const char *str, ...) const; + void drawText(float x, float y, const std::string& str) const; + + std::ostream& beginDraw(float x, float y); + StreamFlusher endDraw(); + + unsigned int calcStringWidth(const std::string& str) const; + unsigned int getHeight() const; + +private: + // leave copy constructor and operator= undefined to make noncopyable + GLFT_Font(const GLFT_Font&); + const GLFT_Font& operator=(const GLFT_Font&); + +private: + // font data + unsigned int texID_; + unsigned int listBase_; + std::vector widths_; + unsigned char height_; + // stream drawing stuff + std::ostringstream ss_; + float drawX_; + float drawY_; + + static const unsigned int SPACE = 32; + static const unsigned int NUM_CHARS = 96; + + static FTLibraryContainer library_; +}; + +#endif //GLFT_FONT_HPP diff --git a/README b/README new file mode 100644 index 0000000..bc0b3a1 --- /dev/null +++ b/README @@ -0,0 +1,34 @@ +// GLFT_Font (http://www.cs.rit.edu/~jpt2433/glftfont/) +// by James Turk (jpt2433@rit.edu) +// Based on work by Marijn Haverbeke (http://marijn.haverbeke.nl) +// +// Version 0.1 - Released 1 July 2005 +// +// Copyright (c) 2005, James Turk +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the GLFT_Font nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +// POSSIBILITY OF SUCH DAMAGE. + +$Id: README,v 1.1 2005/07/18 22:19:16 cozman Exp $ diff --git a/index.html b/index.html new file mode 100644 index 0000000..2561f04 --- /dev/null +++ b/index.html @@ -0,0 +1,55 @@ + +GLFT_Font + +

GLFT_Font

+ +

About GLFT_Font

+

+GLFT_Font is originally by James Turk +although it is based on the work of +Marijn Haverbeke. GLFT_Font allows +programmers to use fonts in their OpenGL applications with great ease on any +platform for which both FreeType2 and +OpenGL are available. +

+ +

Obtaining GLFT_Font

+

+The latest copy of GLFT_Font will be available via this website. +At the moment the latest version is 0.1. +

+ +

Using GLFT_Font

+

+Using GLFT_Font is designed to be extremely simple. The simplest use is: +


+
+#include "GLFT_Font.h"
+
+...
+glEnable(GL_TEXTURE_2D);
+glEnable(GL_BLEND);
+glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
+...
+Font fnt("somefont.ttf", size);  
+...
+fnt.drawText(x, y, "Hello World");
+
+
+GLFT_Font also includes support for finding the width/height of a string and +drawText supports printf-style argument formatting. +

+ +

Changelog

+

+

+

+ +
+

$Id: index.html,v 1.1 2005/07/18 22:19:16 cozman Exp $

+ + + + diff --git a/test.cpp b/test.cpp new file mode 100644 index 0000000..ce0d2ad --- /dev/null +++ b/test.cpp @@ -0,0 +1,53 @@ +// This file is distributed along with GLFT_Font and is in the public domain. +// Compiled with g++ GLFT_Font.cpp test.cpp -o test -Wall -pedantic +// -lglfw -lGL -lfreetype +// +// $Id: test.cpp,v 1.1 2005/07/18 22:19:16 cozman Exp $ + +#include +#include "GLFT_Font.hpp" +#include + +int main(int argc, char** argv) +{ + if(argc != 2) + { + std::cerr << "Usage: test file. Where file is a valid font\n"; + } + else + { + glfwInit(); + glfwOpenWindow(800, 600, 8, 8, 8, 8, 0, 0, GLFW_WINDOW); + + GLFT_Font f; + f.open(argv[1],24); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0, 800, 600, 0, -1.0, 1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glEnable(GL_TEXTURE_2D); + glEnable(GL_BLEND); + glDisable(GL_LIGHTING); + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + + do + { + glClear(GL_COLOR_BUFFER_BIT); + + f.drawText(0, 0, "drawText"); + f.drawText(f.calcStringWidth("drawText"), f.getHeight(), + "drawText 0x%X", 250); + f.beginDraw(200, 200) << "beginDraw: " << 10 << f.endDraw(); + + glfwSwapBuffers(); + + } while(glfwGetWindowParam(GLFW_OPENED) && + glfwGetKey(GLFW_KEY_ESC) != GLFW_PRESS); + + glfwCloseWindow(); + glfwTerminate(); + } +} +