1 // texturefont.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel <claurel@shatters.net>
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 
10 #include <cassert>
11 #include <cstring>
12 #include <fstream>
13 
14 #ifndef _WIN32
15 #ifndef TARGET_OS_MAC
16 #include <config.h>
17 #endif /* TARGET_OS_MAC */
18 #endif /* _WIN32 */
19 
20 #include <celutil/debug.h>
21 #include <celutil/bytes.h>
22 #include <celutil/utf8.h>
23 #include <celutil/util.h>
24 #include <celengine/gl.h>
25 #include "texturefont.h"
26 
27 using namespace std;
28 
29 
TextureFont()30 TextureFont::TextureFont() :
31     maxAscent(0),
32     maxDescent(0),
33     maxWidth(0),
34     texWidth(0),
35     texHeight(0),
36     fontImage(NULL),
37     texName(0),
38     glyphLookup(NULL),
39     glyphLookupTableSize(0)
40 {
41 }
42 
43 
~TextureFont()44 TextureFont::~TextureFont()
45 {
46     if (texName != 0)
47         glDeleteTextures(1, (const GLuint*) &texName);
48     if (fontImage != NULL)
49         delete[] fontImage;
50     if (glyphLookup != NULL)
51         delete[] glyphLookup;
52 }
53 
54 
render(wchar_t ch) const55 void TextureFont::render(wchar_t ch) const
56 {
57     const Glyph* glyph = getGlyph(ch);
58     if (glyph == NULL) glyph = getGlyph((wchar_t)'?');
59     if (glyph != NULL)
60     {
61         glBegin(GL_QUADS);
62         glTexCoord2f(glyph->texCoords[0].u, glyph->texCoords[0].v);
63         glVertex2f(glyph->xoff, glyph->yoff);
64         glTexCoord2f(glyph->texCoords[1].u, glyph->texCoords[1].v);
65         glVertex2f(glyph->xoff + glyph->width, glyph->yoff);
66         glTexCoord2f(glyph->texCoords[2].u, glyph->texCoords[2].v);
67         glVertex2f(glyph->xoff + glyph->width, glyph->yoff + glyph->height);
68         glTexCoord2f(glyph->texCoords[3].u, glyph->texCoords[3].v);
69         glVertex2f(glyph->xoff, glyph->yoff + glyph->height);
70         glEnd();
71         glTranslatef(glyph->advance, 0.0f, 0.0f);
72     }
73 }
74 
75 
render(const string & s) const76 void TextureFont::render(const string& s) const
77 {
78     int len = s.length();
79     bool validChar = true;
80     int i = 0;
81 
82 	while (i < len && validChar) {
83         wchar_t ch = 0;
84         validChar = UTF8Decode(s, i, ch);
85         i += UTF8EncodedSize(ch);
86 
87         render(ch);
88     }
89 }
90 
91 
getWidth(const string & s) const92 int TextureFont::getWidth(const string& s) const
93 {
94     int width = 0;
95     int len = s.length();
96     bool validChar = true;
97 	int i = 0;
98 
99     while (i < len && validChar)
100     {
101         wchar_t ch = 0;
102         validChar = UTF8Decode(s, i, ch);
103         i += UTF8EncodedSize(ch);
104 
105         const Glyph* g = getGlyph(ch);
106         if (g != NULL)
107             width += g->advance;
108     }
109 
110     return width;
111 }
112 
113 
getHeight() const114 int TextureFont::getHeight() const
115 {
116     return maxAscent + maxDescent;
117 }
118 
getMaxWidth() const119 int TextureFont::getMaxWidth() const
120 {
121     return maxWidth;
122 }
123 
getMaxAscent() const124 int TextureFont::getMaxAscent() const
125 {
126     return maxAscent;
127 }
128 
setMaxAscent(int _maxAscent)129 void TextureFont::setMaxAscent(int _maxAscent)
130 {
131     maxAscent = _maxAscent;
132 }
133 
getMaxDescent() const134 int TextureFont::getMaxDescent() const
135 {
136     return maxDescent;
137 }
138 
setMaxDescent(int _maxDescent)139 void TextureFont::setMaxDescent(int _maxDescent)
140 {
141     maxDescent = _maxDescent;
142 }
143 
144 
getTextureName() const145 int TextureFont::getTextureName() const
146 {
147     return texName;
148 }
149 
150 
bind()151 void TextureFont::bind()
152 {
153     if (texName != 0)
154         glBindTexture(GL_TEXTURE_2D, texName);
155 }
156 
157 
addGlyph(const TextureFont::Glyph & g)158 void TextureFont::addGlyph(const TextureFont::Glyph& g)
159 {
160     glyphs.insert(glyphs.end(), g);
161     if (g.width > maxWidth)
162         maxWidth = g.width;
163 }
164 
165 
getGlyph(wchar_t ch) const166 const TextureFont::Glyph* TextureFont::getGlyph(wchar_t ch) const
167 {
168     if (ch >= (wchar_t)glyphLookupTableSize)
169         return NULL;
170     else
171         return glyphLookup[ch];
172 }
173 
174 
buildTexture()175 bool TextureFont::buildTexture()
176 {
177     assert(fontImage != NULL);
178 
179     if (texName != 0)
180         glDeleteTextures(1, (const GLuint*) &texName);
181     glGenTextures(1, (GLuint*) &texName);
182     if (texName == 0)
183     {
184         DPRINTF(0, "Failed to allocate texture object for font.\n");
185         return false;
186     }
187 
188     glBindTexture(GL_TEXTURE_2D, texName);
189 
190     // Don't build mipmaps . . . should really make them an option.
191     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
192     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
193     glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA,
194                  texWidth, texHeight,
195                  0,
196                  GL_ALPHA, GL_UNSIGNED_BYTE,
197                  fontImage);
198 
199     return true;
200 }
201 
202 
rebuildGlyphLookupTable()203 void TextureFont::rebuildGlyphLookupTable()
204 {
205     if (glyphs.size() == 0)
206         return;
207 
208     // Find the largest glyph id
209     int maxID = glyphs[0].__id;
210     vector<Glyph>::const_iterator iter;
211     for (iter = glyphs.begin(); iter != glyphs.end(); iter++)
212     {
213         if (iter->__id > maxID)
214             maxID = iter->__id;
215     }
216 
217     // If there was already a lookup table, delete it.
218     if (glyphLookup != NULL)
219         delete[] glyphLookup;
220 
221     DPRINTF(1, "texturefont: allocating glyph lookup table with %d entries.\n",
222             maxID + 1);
223     glyphLookup = new const Glyph*[maxID + 1];
224     for (int i = 0; i <= maxID; i++)
225         glyphLookup[i] = NULL;
226 
227     // Fill the table with glyph pointers
228     for (iter = glyphs.begin(); iter != glyphs.end(); iter++)
229         glyphLookup[iter->__id] = &(*iter);
230     glyphLookupTableSize = (unsigned int) maxID + 1;
231 }
232 
233 
readUint32(istream & in,bool swap)234 static uint32 readUint32(istream& in, bool swap)
235 {
236     uint32 x;
237     in.read(reinterpret_cast<char*>(&x), sizeof x);
238     return swap ? bswap_32(x) : x;
239 }
240 
readUint16(istream & in,bool swap)241 static uint16 readUint16(istream& in, bool swap)
242 {
243     uint16 x;
244     in.read(reinterpret_cast<char*>(&x), sizeof x);
245     return swap ? bswap_16(x) : x;
246 }
247 
readUint8(istream & in)248 static uint8 readUint8(istream& in)
249 {
250     uint8 x;
251     in.read(reinterpret_cast<char*>(&x), sizeof x);
252     return x;
253 }
254 
255 /* Not currently used
256 static int32 readInt32(istream& in, bool swap)
257 {
258     int32 x;
259     in.read(reinterpret_cast<char*>(&x), sizeof x);
260     return swap ? static_cast<int32>(bswap_32(static_cast<uint32>(x))) : x;
261 }*/
262 
readInt16(istream & in,bool swap)263 static int16 readInt16(istream& in, bool swap)
264 {
265     int16 x;
266     in.read(reinterpret_cast<char*>(&x), sizeof x);
267     return swap ? static_cast<int16>(bswap_16(static_cast<uint16>(x))) : x;
268 }
269 
readInt8(istream & in)270 static int8 readInt8(istream& in)
271 {
272     int8 x;
273     in.read(reinterpret_cast<char*>(&x), sizeof x);
274     return x;
275 }
276 
277 
load(istream & in)278 TextureFont* TextureFont::load(istream& in)
279 {
280     char header[4];
281 
282     in.read(header, sizeof header);
283     if (!in.good() || strncmp(header, "\377txf", 4) != 0)
284     {
285         DPRINTF(0, "Stream is not a texture font!.\n");
286         return NULL;
287     }
288 
289     uint32 endiannessTest = 0;
290     in.read(reinterpret_cast<char*>(&endiannessTest), sizeof endiannessTest);
291     if (!in.good())
292     {
293         DPRINTF(0, "Error reading endianness bytes in txf header.\n");
294         return NULL;
295     }
296 
297     bool byteSwap;
298     if (endiannessTest == 0x78563412)
299         byteSwap = true;
300     else if (endiannessTest == 0x12345678)
301         byteSwap = false;
302     else
303     {
304         DPRINTF(0, "Stream is not a texture font!.\n");
305         return NULL;
306     }
307 
308     int format = readUint32(in, byteSwap);
309     unsigned int texWidth = readUint32(in, byteSwap);
310     unsigned int texHeight = readUint32(in, byteSwap);
311     unsigned int maxAscent = readUint32(in, byteSwap);
312     unsigned int maxDescent = readUint32(in, byteSwap);
313     unsigned int nGlyphs = readUint32(in, byteSwap);
314 
315     if (!in)
316     {
317         DPRINTF(0, "Texture font stream is incomplete");
318         return NULL;
319     }
320 
321     DPRINTF(1, "Font contains %d glyphs.\n", nGlyphs);
322 
323     TextureFont* font = new TextureFont();
324     assert(font != NULL);
325 
326     font->setMaxAscent(maxAscent);
327     font->setMaxDescent(maxDescent);
328 
329     float dx = 0.5f / texWidth;
330     float dy = 0.5f / texHeight;
331 
332     for (unsigned int i = 0; i < nGlyphs; i++)
333     {
334         uint16 __id = readUint16(in, byteSwap);
335         TextureFont::Glyph glyph(__id);
336 
337         glyph.width = readUint8(in);
338         glyph.height = readUint8(in);
339         glyph.xoff = readInt8(in);
340         glyph.yoff = readInt8(in);
341         glyph.advance = readInt8(in);
342         readInt8(in);
343         glyph.x = readInt16(in, byteSwap);
344         glyph.y = readInt16(in, byteSwap);
345 
346         if (!in)
347         {
348             DPRINTF(0, "Error reading glyph %ud from texture font stream.\n", i);
349             delete font;
350             return NULL;
351         }
352 
353         float fWidth = texWidth;
354         float fHeight = texHeight;
355         glyph.texCoords[0].u = glyph.x / fWidth + dx;
356         glyph.texCoords[0].v = glyph.y / fHeight + dy;
357         glyph.texCoords[1].u = (glyph.x + glyph.width) / fWidth + dx;
358         glyph.texCoords[1].v = glyph.y / fHeight + dy;
359         glyph.texCoords[2].u = (glyph.x + glyph.width) / fWidth + dx;
360         glyph.texCoords[2].v = (glyph.y + glyph.height) / fHeight + dy;
361         glyph.texCoords[3].u = glyph.x / fWidth + dx;
362         glyph.texCoords[3].v = (glyph.y + glyph.height) / fHeight + dy;
363 
364         font->addGlyph(glyph);
365     }
366 
367     font->texWidth = texWidth;
368     font->texHeight = texHeight;
369     if (format == TxfByte)
370     {
371         unsigned char* fontImage = new unsigned char[texWidth * texHeight];
372         if (fontImage == NULL)
373         {
374             DPRINTF(0, "Not enough memory for font bitmap.\n");
375             delete font;
376             return NULL;
377         }
378 
379         DPRINTF(1, "Reading %d x %d 8-bit font image.\n", texWidth, texHeight);
380 
381         in.read(reinterpret_cast<char*>(fontImage), texWidth * texHeight);
382         if (in.gcount() != (signed)(texWidth * texHeight))
383         {
384             DPRINTF(0, "Missing bitmap data in font stream.\n");
385             delete font;
386             delete[] fontImage;
387             return NULL;
388         }
389 
390         font->fontImage = fontImage;
391     }
392     else
393     {
394         int rowBytes = (texWidth + 7) >> 3;
395         unsigned char* fontBits = new unsigned char[rowBytes * texHeight];
396         unsigned char* fontImage = new unsigned char[texWidth * texHeight];
397         if (fontImage == NULL || fontBits == NULL)
398         {
399             DPRINTF(0, "Not enough memory for font bitmap.\n");
400             delete font;
401             if (fontBits != NULL)
402                 delete[] fontBits;
403             if (fontImage != NULL)
404                 delete[] fontImage;
405             return NULL;
406         }
407 
408         DPRINTF(1, "Reading %d x %d 1-bit font image.\n", texWidth, texHeight);
409 
410         in.read(reinterpret_cast<char*>(fontBits), rowBytes * texHeight);
411         if (in.gcount() != (signed)(rowBytes * texHeight))
412         {
413             DPRINTF(0, "Missing bitmap data in font stream.\n");
414             delete font;
415             return NULL;
416         }
417 
418         for (unsigned int y = 0; y < texHeight; y++)
419         {
420             for (unsigned int x = 0; x < texWidth; x++)
421             {
422                 if ((fontBits[y * rowBytes + (x >> 3)] & (1 << (x & 0x7))) != 0)
423                     fontImage[y * texWidth + x] = 0xff;
424                 else
425                     fontImage[y * texWidth + x] = 0x0;
426             }
427         }
428 
429         font->fontImage = fontImage;
430         delete[] fontBits;
431     }
432 
433     font->rebuildGlyphLookupTable();
434 
435     return font;
436 }
437 
438 
LoadTextureFont(const string & filename)439 TextureFont* LoadTextureFont(const string& filename)
440 {
441     string localeFilename = LocaleFilename(filename);
442     ifstream in(localeFilename.c_str(), ios::in | ios::binary);
443     if (!in.good())
444     {
445         DPRINTF(0, "Could not open font file %s\n", filename.c_str());
446         return NULL;
447     }
448 
449     return TextureFont::load(in);
450 }
451