00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include <cassert>
00011 #include <cstring>
00012 #include <fstream>
00013
00014 #ifndef _WIN32
00015 #ifndef MACOSX_PB
00016 #include <config.h>
00017 #endif
00018 #endif
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
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
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
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
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
00254
00255
00256
00257
00258
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 }