Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

texturefont.cpp

Go to the documentation of this file.
00001 // texturefont.cpp
00002 //
00003 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
00004 //
00005 // This program is free software; you can redistribute it and/or
00006 // modify it under the terms of the GNU General Public License
00007 // as published by the Free Software Foundation; either version 2
00008 // of the License, or (at your option) any later version.
00009 
00010 #include <cassert>
00011 #include <cstring>
00012 #include <fstream>
00013 
00014 #ifndef _WIN32
00015 #ifndef MACOSX_PB
00016 #include <config.h>
00017 #endif /* MACOSX_PB */
00018 #endif /* _WIN32 */
00019 
00020 #include <celutil/debug.h>
00021 #include <celutil/bytes.h>
00022 #include <celutil/utf8.h>
00023 #include <celengine/gl.h>
00024 #include "texturefont.h"
00025 
00026 using namespace std;
00027 
00028 
00029 TextureFont::TextureFont() :
00030     maxAscent(0),
00031     maxDescent(0),
00032     maxWidth(0),
00033     texWidth(0),
00034     texHeight(0),
00035     fontImage(NULL),
00036     texName(0),
00037     glyphLookup(NULL),
00038     glyphLookupTableSize(0)
00039 {
00040 }
00041 
00042 
00043 TextureFont::~TextureFont()
00044 {
00045     if (texName != 0)
00046         glDeleteTextures(1, (const GLuint*) &texName);
00047     if (fontImage != NULL)
00048         delete[] fontImage;
00049     if (glyphLookup != NULL)
00050         delete[] glyphLookup;
00051 }
00052 
00053 
00054 void TextureFont::render(wchar_t ch) const
00055 {
00056     const Glyph* glyph = getGlyph(ch);
00057     if (glyph != NULL)
00058     {
00059         glBegin(GL_QUADS);
00060         glTexCoord2f(glyph->texCoords[0].u, glyph->texCoords[0].v);
00061         glVertex2f(glyph->xoff, glyph->yoff);
00062         glTexCoord2f(glyph->texCoords[1].u, glyph->texCoords[1].v);
00063         glVertex2f(glyph->xoff + glyph->width, glyph->yoff);
00064         glTexCoord2f(glyph->texCoords[2].u, glyph->texCoords[2].v);
00065         glVertex2f(glyph->xoff + glyph->width, glyph->yoff + glyph->height);
00066         glTexCoord2f(glyph->texCoords[3].u, glyph->texCoords[3].v);
00067         glVertex2f(glyph->xoff, glyph->yoff + glyph->height);
00068         glEnd();
00069         glTranslatef(glyph->advance, 0.0f, 0.0f);
00070     }
00071 }
00072 
00073 
00074 void TextureFont::render(const string& s) const
00075 {
00076     int len = s.length();
00077     bool validChar = true;
00078     int i = 0;
00079         
00080         while (i < len && validChar) {
00081         wchar_t ch = 0;
00082         validChar = UTF8Decode(s, i, ch);
00083         i += UTF8EncodedSize(ch);
00084         
00085         render(ch);
00086     }
00087 }
00088 
00089 
00090 int TextureFont::getWidth(const string& s) const
00091 {
00092     int width = 0;
00093     int len = s.length();
00094     bool validChar = true;
00095         int i = 0;
00096 
00097     while (i < len && validChar)
00098     {
00099         wchar_t ch = 0;
00100         validChar = UTF8Decode(s, i, ch);
00101         i += UTF8EncodedSize(ch);
00102 
00103         const Glyph* g = getGlyph(ch);
00104         if (g != NULL)
00105             width += g->advance;
00106     }
00107 
00108     return width;
00109 }
00110 
00111 
00112 int TextureFont::getHeight() const
00113 {
00114     return maxAscent + maxDescent;
00115 }
00116 
00117 int TextureFont::getMaxWidth() const
00118 {
00119     return maxWidth;
00120 }
00121 
00122 int TextureFont::getMaxAscent() const
00123 {
00124     return maxAscent;
00125 }
00126 
00127 void TextureFont::setMaxAscent(int _maxAscent)
00128 {
00129     maxAscent = _maxAscent;
00130 }
00131 
00132 int TextureFont::getMaxDescent() const
00133 {
00134     return maxDescent;
00135 }
00136 
00137 void TextureFont::setMaxDescent(int _maxDescent)
00138 {
00139     maxDescent = _maxDescent;
00140 }
00141 
00142 
00143 int TextureFont::getTextureName() const
00144 {
00145     return texName;
00146 }
00147 
00148 
00149 void TextureFont::bind()
00150 {
00151     if (texName != 0)
00152         glBindTexture(GL_TEXTURE_2D, texName);
00153 }
00154 
00155 
00156 void TextureFont::addGlyph(const TextureFont::Glyph& g)
00157 {
00158     glyphs.insert(glyphs.end(), g);
00159     if (g.width > maxWidth)
00160         maxWidth = g.width;
00161 }
00162 
00163 
00164 const TextureFont::Glyph* TextureFont::getGlyph(wchar_t ch) const
00165 {
00166     if (ch >= (wchar_t)glyphLookupTableSize)
00167         return NULL;
00168     else
00169         return glyphLookup[ch];
00170 }
00171 
00172 
00173 bool TextureFont::buildTexture()
00174 {
00175     assert(fontImage != NULL);
00176 
00177     if (texName != 0)
00178         glDeleteTextures(1, (const GLuint*) &texName);
00179     glGenTextures(1, (GLuint*) &texName);
00180     if (texName == 0)
00181     {
00182         DPRINTF(0, "Failed to allocate texture object for font.\n");
00183         return false;
00184     }
00185 
00186     glBindTexture(GL_TEXTURE_2D, texName);
00187 
00188     // Don't build mipmaps . . . should really make them an option.
00189     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
00190     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
00191     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
00192                  texWidth, texHeight,
00193                  0,
00194                  GL_ALPHA, GL_UNSIGNED_BYTE,
00195                  fontImage);
00196 
00197     return true;
00198 }
00199 
00200 
00201 void TextureFont::rebuildGlyphLookupTable()
00202 {
00203     if (glyphs.size() == 0)
00204         return;
00205 
00206     // Find the largest glyph id
00207     int maxID = glyphs[0].__id;
00208     vector<Glyph>::const_iterator iter;
00209     for (iter = glyphs.begin(); iter != glyphs.end(); iter++)
00210     {
00211         if (iter->__id > maxID)
00212             maxID = iter->__id;
00213     }
00214 
00215     // If there was already a lookup table, delete it.
00216     if (glyphLookup != NULL)
00217         delete[] glyphLookup;
00218 
00219     DPRINTF(1, "texturefont: allocating glyph lookup table with %d entries.\n",
00220             maxID + 1);
00221     glyphLookup = new const Glyph*[maxID + 1];
00222     for (int i = 0; i <= maxID; i++)
00223         glyphLookup[i] = NULL;
00224 
00225     // Fill the table with glyph pointers
00226     for (iter = glyphs.begin(); iter != glyphs.end(); iter++)
00227         glyphLookup[iter->__id] = &(*iter);
00228     glyphLookupTableSize = (unsigned int) maxID + 1;
00229 }
00230 
00231 
00232 static uint32 readUint32(istream& in, bool swap)
00233 {
00234     uint32 x;
00235     in.read(reinterpret_cast<char*>(&x), sizeof x);
00236     return swap ? bswap_32(x) : x;
00237 }
00238 
00239 static uint16 readUint16(istream& in, bool swap)
00240 {
00241     uint16 x;
00242     in.read(reinterpret_cast<char*>(&x), sizeof x);
00243     return swap ? bswap_16(x) : x;
00244 }
00245 
00246 static uint8 readUint8(istream& in)
00247 {
00248     uint8 x;
00249     in.read(reinterpret_cast<char*>(&x), sizeof x);
00250     return x;
00251 }
00252 
00253 /* Not currently used
00254 static int32 readInt32(istream& in, bool swap)
00255 {
00256     int32 x;
00257     in.read(reinterpret_cast<char*>(&x), sizeof x);
00258     return swap ? static_cast<int32>(bswap_32(static_cast<uint32>(x))) : x;
00259 }*/
00260 
00261 static int16 readInt16(istream& in, bool swap)
00262 {
00263     int16 x;
00264     in.read(reinterpret_cast<char*>(&x), sizeof x);
00265     return swap ? static_cast<int16>(bswap_16(static_cast<uint16>(x))) : x;
00266 }
00267 
00268 static int8 readInt8(istream& in)
00269 {
00270     int8 x;
00271     in.read(reinterpret_cast<char*>(&x), sizeof x);
00272     return x;
00273 }
00274 
00275 
00276 TextureFont* TextureFont::load(istream& in)
00277 {
00278     char header[4];
00279 
00280     in.read(header, sizeof header);
00281     if (!in.good() || strncmp(header, "\377txf", 4) != 0)
00282     {
00283         DPRINTF(0, "Stream is not a texture font!.\n");
00284         return NULL;
00285     }
00286 
00287     uint32 endiannessTest = 0;
00288     in.read(reinterpret_cast<char*>(&endiannessTest), sizeof endiannessTest);
00289     if (!in.good())
00290     {
00291         DPRINTF(0, "Error reading endianness bytes in txf header.\n");
00292         return NULL;
00293     }
00294 
00295     bool byteSwap;
00296     if (endiannessTest == 0x78563412)
00297         byteSwap = true;
00298     else if (endiannessTest == 0x12345678)
00299         byteSwap = false;
00300     else
00301     {
00302         DPRINTF(0, "Stream is not a texture font!.\n");
00303         return NULL;
00304     }
00305 
00306     int format = readUint32(in, byteSwap);
00307     unsigned int texWidth = readUint32(in, byteSwap);
00308     unsigned int texHeight = readUint32(in, byteSwap);
00309     unsigned int maxAscent = readUint32(in, byteSwap);
00310     unsigned int maxDescent = readUint32(in, byteSwap);
00311     unsigned int nGlyphs = readUint32(in, byteSwap);
00312 
00313     if (!in)
00314     {
00315         DPRINTF(0, "Texture font stream is incomplete");
00316         return NULL;
00317     }
00318 
00319     DPRINTF(1, "Font contains %d glyphs.\n", nGlyphs);
00320 
00321     TextureFont* font = new TextureFont();
00322     assert(font != NULL);
00323 
00324     font->setMaxAscent(maxAscent);
00325     font->setMaxDescent(maxDescent);
00326 
00327     float dx = 0.5f / texWidth;
00328     float dy = 0.5f / texHeight;
00329 
00330     for (unsigned int i = 0; i < nGlyphs; i++)
00331     {
00332         uint16 __id = readUint16(in, byteSwap);
00333         TextureFont::Glyph glyph(__id);
00334 
00335         glyph.width = readUint8(in);
00336         glyph.height = readUint8(in);
00337         glyph.xoff = readInt8(in);
00338         glyph.yoff = readInt8(in);
00339         glyph.advance = readInt8(in);
00340         readInt8(in);
00341         glyph.x = readInt16(in, byteSwap);
00342         glyph.y = readInt16(in, byteSwap);
00343         
00344         if (!in)
00345         {
00346             DPRINTF(0, "Error reading glyph %ud from texture font stream.\n", i);
00347             delete font;
00348             return NULL;
00349         }
00350 
00351         float fWidth = texWidth;
00352         float fHeight = texHeight;
00353         glyph.texCoords[0].u = glyph.x / fWidth + dx;
00354         glyph.texCoords[0].v = glyph.y / fHeight + dy;
00355         glyph.texCoords[1].u = (glyph.x + glyph.width) / fWidth + dx;
00356         glyph.texCoords[1].v = glyph.y / fHeight + dy;
00357         glyph.texCoords[2].u = (glyph.x + glyph.width) / fWidth + dx;
00358         glyph.texCoords[2].v = (glyph.y + glyph.height) / fHeight + dy;
00359         glyph.texCoords[3].u = glyph.x / fWidth + dx;
00360         glyph.texCoords[3].v = (glyph.y + glyph.height) / fHeight + dy;
00361 
00362         font->addGlyph(glyph);
00363     }
00364 
00365     font->texWidth = texWidth;
00366     font->texHeight = texHeight;
00367     if (format == TxfByte)
00368     {
00369         unsigned char* fontImage = new unsigned char[texWidth * texHeight];
00370         if (fontImage == NULL)
00371         {
00372             DPRINTF(0, "Not enough memory for font bitmap.\n");
00373             delete font;
00374             return NULL;
00375         }
00376 
00377         DPRINTF(1, "Reading %d x %d 8-bit font image.\n", texWidth, texHeight);
00378 
00379         in.read(reinterpret_cast<char*>(fontImage), texWidth * texHeight);
00380         if (in.gcount() != (signed)(texWidth * texHeight))
00381         {
00382             DPRINTF(0, "Missing bitmap data in font stream.\n");
00383             delete font;
00384             delete[] fontImage;
00385             return NULL;
00386         }
00387 
00388         font->fontImage = fontImage;
00389     }
00390     else
00391     {
00392         int rowBytes = (texWidth + 7) >> 3;
00393         unsigned char* fontBits = new unsigned char[rowBytes * texHeight];
00394         unsigned char* fontImage = new unsigned char[texWidth * texHeight];
00395         if (fontImage == NULL || fontBits == NULL)
00396         {
00397             DPRINTF(0, "Not enough memory for font bitmap.\n");
00398             delete font;
00399             if (fontBits != NULL)
00400                 delete[] fontBits;
00401             if (fontImage != NULL)
00402                 delete[] fontImage;
00403             return NULL;
00404         }
00405 
00406         DPRINTF(1, "Reading %d x %d 1-bit font image.\n", texWidth, texHeight);
00407 
00408         in.read(reinterpret_cast<char*>(fontBits), rowBytes * texHeight);
00409         if (in.gcount() != (signed)(rowBytes * texHeight))
00410         {
00411             DPRINTF(0, "Missing bitmap data in font stream.\n");
00412             delete font;
00413             return NULL;
00414         }
00415 
00416         for (unsigned int y = 0; y < texHeight; y++)
00417         {
00418             for (unsigned int x = 0; x < texWidth; x++)
00419             {
00420                 if ((fontBits[y * rowBytes + (x >> 3)] & (1 << (x & 0x7))) != 0)
00421                     fontImage[y * texWidth + x] = 0xff;
00422                 else
00423                     fontImage[y * texWidth + x] = 0x0;
00424             }
00425         }
00426 
00427         font->fontImage = fontImage;
00428         delete[] fontBits;
00429     }
00430 
00431     font->rebuildGlyphLookupTable();
00432 
00433     return font;
00434 }
00435 
00436 
00437 TextureFont* LoadTextureFont(const string& filename)
00438 {
00439     ifstream in(filename.c_str(), ios::in | ios::binary);
00440     if (!in.good())
00441     {
00442         DPRINTF(0, "Could not open font file %s\n", filename.c_str());
00443         return NULL;
00444     }
00445 
00446     return TextureFont::load(in);
00447 }

Generated on Sat Jan 14 22:30:32 2006 for Celestia by  doxygen 1.4.1