From: Markus R. <rol...@us...> - 2005-12-05 21:38:30
|
Update of /cvsroot/simspark/simspark/spark/kerosin/fontserver In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv19774/fontserver Added Files: .cvsignore font.cpp font.h fontserver.cpp fontserver.h fontserver_c.cpp glyph.cpp glyph.h Log Message: --- NEW FILE: .cvsignore --- *.lo .deps .dirstamp .libs --- NEW FILE: font.cpp --- #include "font.h" #include <stdio.h> #include <stdarg.h> #include <salt/gmath.h> #include <boost/scoped_array.hpp> #include <zeitgeist/logserver/logserver.h> #include <zeitgeist/scriptserver/scriptserver.h> #include "fontserver.h" #if defined(_WIN32) && !defined(APIENTRY) && !defined(__CYGWIN__) #define WIN32_LEAN_AND_MEAN 1 #include <windows.h> #endif #include <GL/gl.h> using namespace kerosin; using namespace salt; Font::Font(FontServer &fontServer) : mTexID(0), mRowHeight(0), mSize(0), mFontServer(fontServer) { } Font::~Font() { } bool Font::Init(const std::string &name, unsigned int size, FT_Face face) { mName = name; mSize = size; if (mTexID != 0) { glDeleteTextures(1, &mTexID); mTexID = 0; GLenum error = glGetError(); if (error) { mFontServer.GetLog()->Debug() << "(Font) ERROR: glGetError() reports error " << error << " after deleting my font texture\n"; return false; } } int dpiHRes, dpiVRes; bool getConfig = ( mFontServer.GetScript()->GetVariable("Viewport.DpiHRes", dpiHRes) && mFontServer.GetScript()->GetVariable("Viewport.DpiVRes", dpiVRes) ); if (! getConfig) { mFontServer.GetLog()->Debug() << "(Font) ERROR: cannot read Viewport dpi values from ScriptServer\n"; return false; } FT_Set_Char_Size( face, /* handle to face object */ 0, /* char_width in 1/64th of points */ 64*size, /* char_height in 1/64th of points */ dpiHRes, /* horizontal device resolution */ dpiVRes /* vertical device resolution */ ); // x resolution of font texture unsigned int x = 256; // y resolution of font texture ... to be determined unsigned int y = 0; // the current maximum height unsigned int curMaxHeight = 0; // we also want to determine the (maximum) height of a row mRowHeight = 0; // here we will store the maximum height of each row std::vector<unsigned int> heights; // first pass to determine dimensions Glyph glyph; for(int i=32; i<128; ++i) { if (! glyph.LoadGlyph(face, i)) { mFontServer.GetLog()->Debug() << "(Font) ERROR: LoadGlyph() failed in first pass\n"; return false; } curMaxHeight = gMax(curMaxHeight, glyph.mByteHeight); if(x+glyph.mByteWidth > 256) { // move to next row x = glyph.mByteWidth+1; y+= curMaxHeight+1; mRowHeight = gMax(mRowHeight, curMaxHeight); heights.push_back(curMaxHeight); curMaxHeight = 0; } else { x+= glyph.mByteWidth+1; } } // calculate the texture height (has to be power of two) unsigned int requiredHeight = 1; y+=1; while(requiredHeight < y) { requiredHeight*=2; } // setup image buffer now we create a texture that is big enough for // this data boost::scoped_array<unsigned char> imageBuffer (new unsigned char[256*requiredHeight]); // clear the image memset(imageBuffer.get(), 0x00, 256*requiredHeight*sizeof(unsigned char)); // copy glyphs to the imageBuffer x=0; y=0; curMaxHeight = 0; // loop through our characters for(int i=32; i<128; ++i) { if (glyph.LoadGlyph(face, i) == false) { mFontServer.GetLog()->Debug() << "(Font) ERROR: LoadGlyph() failed in second pass\n"; return false; } curMaxHeight = gMax(curMaxHeight, glyph.mByteHeight); if(x+glyph.mByteWidth+1>256) { x=0; y+=curMaxHeight+1; // height of a row of characters plus 2 pixels space curMaxHeight = 0; } if(glyph.mByteWidth*glyph.mByteHeight > 0) { for (unsigned int copyY = 0; copyY < glyph.mByteHeight; ++copyY) { for (unsigned int copyX = 0; copyX < glyph.mByteWidth; ++copyX) { imageBuffer[(y+1+copyY)*256 + x+1+copyX] = glyph.mData[copyY*glyph.mByteWidth + copyX]; } } mMetrics[i-32].mByteWidth = glyph.mByteWidth; mMetrics[i-32].mByteHeight = glyph.mByteHeight; mMetrics[i-32].mXOffset = glyph.mXOffset; mMetrics[i-32].mYOffset = glyph.mYOffset; mMetrics[i-32].mAdvance = glyph.mAdvance; mMetrics[i-32].mTC1.Set((x+0.5f)/256.0f, (y+0.5f)/(float)requiredHeight); mMetrics[i-32].mTC2.Set((x+1.5f+glyph.mByteWidth)/256.0f, (y+1.5f+glyph.mByteHeight)/(float)requiredHeight); } x += glyph.mByteWidth+1; } // Spacing of a blank should be similar to 'i' mMetrics[' '-32].mAdvance = mMetrics['i'-32].mAdvance; glGenTextures(1, &mTexID); GLenum error = glGetError(); if (error) { mFontServer.GetLog()->Debug() << "(Font) ERROR: glGetError() reports error " << error << " after generating my font texture()\n"; mTexID = 0; return false; } glBindTexture(GL_TEXTURE_2D, mTexID); glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, 256, requiredHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, imageBuffer.get()); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); error = glGetError(); if (error) { mFontServer.GetLog()->Debug() << "(Font) ERROR: glGetError() reports error " << error << " after binding my font texture()\n"; return false; } return true; } bool Font::Bind(int vRows) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, mTexID); glMatrixMode(GL_PROJECTION); glPushMatrix(); glLoadIdentity(); // int xRes; // int yRes; //if (!mFontServer.GetScript()->GetVariable("Viewport.xRes", xRes)) return false; //if (!mFontServer.GetScript()->GetVariable("Viewport.yRes", yRes)) return false; if(vRows == -1) { glOrtho(0, 1024, 768, 0, 1, 1000); } else { glOrtho(0, 1024*(mRowHeight*vRows/(float)768), mRowHeight*vRows, 0, 1, 1000); } return true; } void Font::Dump() { glBegin(GL_QUADS); glTexCoord2f(0, 0); glVertex2f(0,0); glTexCoord2f(1, 0); glVertex2f(255, 0); glTexCoord2f(1, 1); glVertex2f(255, 255); glTexCoord2f(0, 1); glVertex2f(0, 255); glEnd(); } void Font::DrawString(float x, float y, const char *string) { const char *c = string; float curx; float cury; y += mRowHeight; while(*c != 0) { int i = *c-32; // rebase the index if(i>=0 && i<96) { GlyphMetric &metric = mMetrics[i]; curx = x; cury = y; curx+= metric.mXOffset; cury-= metric.mYOffset; glBegin(GL_QUADS); glTexCoord2f(metric.mTC1.x(), metric.mTC1.y()); glVertex2f(curx,cury); glTexCoord2f(metric.mTC2.x(), metric.mTC1.y()); glVertex2f(curx+metric.mByteWidth, cury); glTexCoord2f(metric.mTC2.x(), metric.mTC2.y()); glVertex2f(curx+metric.mByteWidth, cury+metric.mByteHeight); glTexCoord2f(metric.mTC1.x(), metric.mTC2.y()); glVertex2f(curx, cury+metric.mByteHeight); glEnd(); x+=(float)metric.mAdvance; } ++c; } } void Font::Printf(float x, float y, const char *format, ...) { char buffer[4096]; va_list args; va_start(args, format); vsprintf(buffer, format, args); va_end(args); DrawString(x, y, buffer); } void Font::RowPrintf(float x, float row, const char *format, ...) { char buffer[4096]; va_list args; va_start(args, format); vsprintf(buffer, format, args); va_end(args); DrawString(x, row*mRowHeight, buffer); } float Font::GetStringWidth(const char* string, int numChar) { if (numChar == -1) numChar = strlen(string); const char *c = string; float len = 0; int chCount = 0; while ( (*c != 0) && (chCount < numChar) ) { int i = *c-32; // rebase the index if(i>=0 && i<96) { GlyphMetric &metric = mMetrics[i]; len += metric.mAdvance; } ++c; ++chCount; } return len; } float Font::GetRowHeight() { return (float)mRowHeight; } const std::string& Font::GetName() const { return mName; } unsigned int Font::GetSize() const { return mSize; } --- NEW FILE: glyph.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: glyph.h,v 1.1 2005/12/05 21:38:22 rollmark Exp $ 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef KEROSIN_GLYPH_H #define KEROSIN_GLYPH_H // strange as this may seem #include <ft2build.h> #include FT_FREETYPE_H #include <boost/shared_array.hpp> namespace kerosin { class Glyph { public: Glyph(); bool LoadGlyph(FT_Face face, unsigned int charCode); unsigned int mByteWidth; unsigned int mByteHeight; unsigned int mXOffset; unsigned int mYOffset; unsigned int mAdvance; boost::shared_array<unsigned char> mData; private: void Reset(); }; } //namespace kerosin #endif //KEROSIN_GLYPH_H --- NEW FILE: glyph.cpp --- #include "glyph.h" using namespace kerosin; Glyph::Glyph() { Reset(); } bool Glyph::LoadGlyph(FT_Face face, unsigned int charCode) { int error = FT_Load_Char( face, charCode, FT_LOAD_RENDER); if (error) { return false; } Reset(); mByteWidth = face->glyph->bitmap.width; mByteHeight = face->glyph->bitmap.rows; mData.reset(new unsigned char[mByteWidth*mByteHeight]); //printf("Glyph: '%c' %d\n", charCode, face->glyph->bitmap.pitch); //printf(" Res: %dx%d\n", face->glyph->bitmap.width, face->glyph->bitmap.rows); for(int y=0; y<face->glyph->bitmap.rows; ++y) { for(int x=0; x<face->glyph->bitmap.width; ++x) { mData[y*mByteWidth + x] = face->glyph->bitmap.buffer[y*face->glyph->bitmap.pitch + x]; } } mXOffset = face->glyph->metrics.horiBearingX >> 6; mYOffset = face->glyph->metrics.horiBearingY >> 6; mAdvance = face->glyph->advance.x >> 6; //printf(" Offset: %dx%d\n", mXOffset, mYOffset); //printf(" Advance:%d\n", mAdvance); return true; } void Glyph::Reset() { mByteWidth = 0; mByteHeight = 0; mXOffset = 0; mYOffset = 0; mAdvance = 0; mData.reset(NULL); } --- NEW FILE: fontserver_c.cpp --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: fontserver_c.cpp,v 1.1 2005/12/05 21:38:22 rollmark Exp $ 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #include "fontserver.h" using namespace boost; using namespace kerosin; using namespace zeitgeist; using namespace std; FUNCTION(FontServer,getFont) { string inName; int inSize; if ( (in.GetSize() != 2) || (! in.GetValue(in[0], inName)) || (! in.GetValue(in[1], inSize)) ) { return false; } return (obj->GetFont(inName,inSize).get() != 0); } void CLASS(FontServer)::DefineClass() { DEFINE_BASECLASS(zeitgeist/Leaf); DEFINE_FUNCTION(getFont); } --- NEW FILE: font.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: font.h,v 1.1 2005/12/05 21:38:22 rollmark Exp $ 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef KEROSIN_FONT_H #define KEROSIN_FONT_H #include <string> #include <salt/vector.h> #include "glyph.h" namespace kerosin { class FontServer; /** Font allows the use of a 'texture'-based font. The font is loaded from Fluid Studios Font Files, which can be generated with a small utility from any Windows font. Care has to be taken, that the resulting font will fit in a 256x256 texture! Our font will only contain characters for the ASCII value range 32-128 ... this should cover the major alphanumeric characters! NOTE: HISTORY: 08.10.01 - MK - Initial version 12.10.01 - MK - This now supports fonts generated with Fluid Studios Font Generator 16.10.01 - MK - Calculates minimum texture size required - Only needs one pixel in-between characters when placing them in the texture 16.10.01 - MR - Added support to calculate the width of a string printed with this font 02.10.02 - MK - Moved to Kerosin (major changes) TODO: - better texture usage TOFIX: - ImageServer activation hack (see FIXME in cpp file) */ class Font { private: struct GlyphMetric { unsigned int mByteWidth; unsigned int mByteHeight; unsigned int mXOffset; unsigned int mYOffset; unsigned int mAdvance; salt::Vector2f mTC1; salt::Vector2f mTC2; }; public: Font(FontServer &fontServer); ~Font(); bool Init(const std::string &name, unsigned int size, FT_Face face); bool Bind(int vRows = -1); void Dump(); void DrawString(float x, float y, const char *string); void Printf(float x, float y, const char *format, ...); void RowPrintf(float x, float row, const char *format, ...); /**! calculates the width of a string, printed with this font. Set numChar to a value between 1 and strlen(string) to calculate intermediate string lengths. A value of -1 (which is the default value) calculates the width if the whole string */ float GetStringWidth(const char* string, int numChar = -1); //! returns the height in pixels of a row float GetRowHeight(); //! returns the name of the font const std::string& GetName() const; //! returns the size of the font unsigned int GetSize() const; private: //! the metrics of all glyphs GlyphMetric mMetrics[96]; //! OpenGL Texture ID unsigned int mTexID; //! height (in pixels) of a row unsigned int mRowHeight; //! font name std::string mName; //! size of font unsigned int mSize; //! reference to the fontserver FontServer& mFontServer; }; } // namespace kerosin #endif //KEROSIN_FONT_H --- NEW FILE: fontserver.h --- /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil -*- this file is part of rcssserver3D Fri May 9 2003 Copyright (C) 2002,2003 Koblenz University Copyright (C) 2003 RoboCup Soccer Server 3D Maintenance Group $Id: fontserver.h,v 1.1 2005/12/05 21:38:22 rollmark Exp $ 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; version 2 of the License. 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, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ #ifndef KEROSIN_FONTSERVER_H #define KEROSIN_FONTSERVER_H #include <zeitgeist/leaf.h> #include <zeitgeist/class.h> // strange as this may seem #include <ft2build.h> #include FT_FREETYPE_H namespace kerosin { class Font; /* The fontserver manages Font objects. It prevents a single font from being loaded several times by the runtime system. NOTE: HISTORY: 15.10.01 - MK - Initial version 02.10.02 - MK - Moved to Kerosin (major changes) TODO: TOFIX: */ class FontServer : public zeitgeist::Leaf { public: FontServer(); ~FontServer(); //! load a font with a given size boost::shared_ptr<Font> GetFont(const std::string &name, unsigned int size = 12); //! test if a specific font has been loaded boost::shared_ptr<Font> FindFont(const std::string &name, unsigned int size) const; //! setup opengl states for font rendering void Begin(); //! reset opengl states after font rendering void End(); protected: bool LoadFont(const std::string &name, unsigned int size, boost::shared_ptr<Font> &font); private: typedef std::list<boost::shared_ptr<kerosin::Font> > TFontList; //! the registry of loaded fonts TFontList mFonts; //! FreeType FT_Library mFreeTypeLib; }; DECLARE_CLASS(FontServer); } //namespace kerosin #endif //KEROSIN_FONTSERVER_H --- NEW FILE: fontserver.cpp --- #include "fontserver.h" #include "font.h" #include <boost/scoped_array.hpp> #include <salt/fileclasses.h> #include <zeitgeist/fileserver/fileserver.h> #include <zeitgeist/logserver/logserver.h> #include <zeitgeist/scriptserver/scriptserver.h> #include <kerosin/openglserver/openglserver.h> using namespace kerosin; using namespace std; using namespace boost; FontServer::FontServer() { FT_Init_FreeType(&mFreeTypeLib); } FontServer::~FontServer() { mFonts.clear(); } shared_ptr<kerosin::Font> FontServer::GetFont(const string &name, unsigned int size) { shared_ptr<kerosin::Font> theFont = FindFont(name, size); if(theFont.get() != 0) { return theFont; } // we don't have a cached entry, so we have to create // a new font object and insert it into our registry theFont.reset(new Font(*this)); if(! LoadFont(name, size, theFont)) { return shared_ptr<kerosin::Font>(); } // insert the value in our registry mFonts.push_back(theFont); return theFont; } shared_ptr<kerosin::Font> FontServer::FindFont(const string &name, unsigned int size) const { for (TFontList::const_iterator i = mFonts.begin(); i != mFonts.end(); ++i) { if ( ((*i)->GetName().compare(name) == 0) && ((*i)->GetSize() == size) ) { return (*i); } } return shared_ptr<kerosin::Font>(); } bool FontServer::LoadFont(const string &name, unsigned int size, shared_ptr<kerosin::Font> &font) { shared_ptr<salt::RFile> file = GetFile()->Open(name.c_str()); if (file.get() == 0) { // try with prefixed fontPath string fontPath; if(GetScript()->GetVariable("System.FontPath", fontPath)) { file = GetFile()->Open((fontPath+name).c_str()); } } if (file.get() == 0) { GetLog()->Error() << "(FontServer) ERROR: font file '" << name << "' not found\n"; return false; } unsigned int fileSize = file->Size(); scoped_array<unsigned char> buffer(new unsigned char[fileSize]); file->Read(buffer.get(), fileSize); FT_Face face; int error = FT_New_Memory_Face(mFreeTypeLib, buffer.get(), fileSize, 0, &face); if (error == FT_Err_Unknown_File_Format) { GetLog()->Error() << "(FontServer) ERROR: Unknown file format\n"; } else if (error) { GetLog()->Error() << "(FontServer) ERROR: Could not create face\n"; } if (error) { FT_Done_Face(face); return false; } bool ok = font->Init(name, size, face); FT_Done_Face(face); return ok; } void FontServer::Begin() { glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glDisable(GL_CULL_FACE); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(0,0,-1); glColor4f(1,1,1,1); } void FontServer::End() { glDisable(GL_BLEND); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glMatrixMode(GL_PROJECTION); glPopMatrix(); } |