Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/trunk/src/lib/graphics/text_engine/font.cc @ 5420

Last change on this file since 5420 was 5420, checked in by bensch, 19 years ago

orxonox/trunk: Font is now Right, and the Rendering 'should be' faster.

File size: 16.5 KB
Line 
1/*
2   orxonox - the future of 3D-vertical-scrollers
3
4   Copyright (C) 2004 orx
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   ### File Specific:
12   main-programmer: Benjamin Grauer
13   co-programmer: ...
14*/
15
16#define DEBUG_SPECIAL_MODULE DEBUG_MODULE_GRAPHICS
17
18#include "font.h"
19#include "text.h"
20
21#ifdef HAVE_SDL_IMAGE_H
22#include <SDL_image.h>
23#else
24#include <SDL/SDL_image.h>
25#endif
26#include "default_font.xpm"
27
28#include "debug.h"
29#include "compiler.h"
30
31using namespace std;
32
33/**
34 * constructs a Font out of a TTF-FIle
35 * @param fontFile the File to load the font from
36 * @param fontSize the Size of the Font in Pixels
37 */
38Font::Font(const char* fontFile, unsigned int renderSize)
39{
40  this->init();
41
42  this->renderSize = renderSize;
43  this->setStyle("c");
44
45  if (fontFile != NULL)
46    this->loadFontFromTTF(fontFile);
47}
48
49/**
50 * constructs a Font out of an ImageFile
51 * @param imageFile the ImageFile to load the Font From.
52 */
53Font::Font(const char* imageFile)
54{
55  this->init();
56  this->setName(imageFile);
57  //  this->setSize(fontSize);
58  SDL_Surface* image = NULL;
59  if (imageFile != NULL)
60    image = IMG_Load(imageFile);
61  else
62    return;
63  if (image != NULL)
64  {
65    this->loadFontFromSDL_Surface(image);
66    SDL_FreeSurface(image);
67  }
68  else
69    PRINTF(1)("loading from surface %s failed: %s\n", imageFile, IMG_GetError());
70}
71
72/**
73 * constructs a Font
74 * @param xpmArray the xpm-ARRAY to load the font from
75 */
76Font::Font(char** xpmArray)
77{
78  this->init();
79  this->setName("XPM-array-font");
80  //  this->setSize(fontSize);
81  SDL_Surface* image = NULL;
82  if (xpmArray != NULL)
83    image = IMG_ReadXPMFromArray(xpmArray);
84  if (image != NULL)
85  {
86    this->loadFontFromSDL_Surface(image);
87    SDL_FreeSurface(image);
88  }
89  else
90    PRINTF(1)("loading from surface failed: %s\n", IMG_GetError());
91}
92
93
94/**
95 * destructs a font
96 * this releases the memory a font uses to be opened.
97 * deletes the glLists, and the TTF-handler, if present.
98 */
99Font::~Font()
100{
101  // deleting all Glyphs
102  if (this->glyphArray != NULL)
103  {
104    for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++)
105    {
106      if (this->glyphArray[i] != NULL)
107      {
108        delete this->glyphArray[i];
109      }
110    }
111    delete[] this->glyphArray;
112  }
113
114  //! @todo check if we really do not need to delete the fastTextureID here.
115//   if (this->fastTextureID != 0)
116//     if(glIsTexture(this->fastTextureID))
117//       glDeleteTextures(1, &this->fastTextureID);
118
119  // erease this font out of the memory.
120  if (likely(this->fontTTF != NULL))
121    TTF_CloseFont(this->fontTTF);
122}
123
124/**
125 * initializes a Font (with default values)
126 */
127void Font::init()
128{
129  this->setClassID(CL_FONT, "Font");
130  // setting default values.
131  this->fontTTF = NULL;
132  this->glyphArray = NULL;
133  this->fastTextureID = 0;
134}
135
136
137/**
138 * sets The Font.
139 * @param fontFile The file containing the font.
140 * @returns true if loaded, false if something went wrong, or if a font was loaded before.
141 */
142bool Font::loadFontFromTTF(const char* fontFile)
143{
144  // checking for existent Font.
145  if (this->fontTTF != NULL)
146  {
147    TTF_CloseFont(this->fontTTF);
148    this->fontTTF = NULL;
149  }
150  if (this->fastTextureID != 0)
151  {
152    if(glIsTexture(this->fastTextureID))
153      glDeleteTextures(1, &this->fastTextureID);
154    this->fastTextureID = 0;
155  }
156
157  this->setName(fontFile);
158  this->fontTTF = TTF_OpenFont(this->getName(), this->renderSize);
159
160  if(this->fontTTF != NULL)
161  {
162    this->fastTextureID = this->createFastTexture();
163    if (this->fastTextureID != 0)
164      return true;
165    else
166      return false;
167  }
168  else
169  {
170    PRINTF(1)("TTF_OpenFont: %s\n", TTF_GetError());
171    return false;
172  }
173
174}
175
176/**
177 * loads a font From an XPM-array.
178 * @param xpmArray the array of the XPM to load the font from.
179 */
180bool Font::loadFontFromSDL_Surface(SDL_Surface* surface)
181{
182  // loading to a texture.
183  if(surface == NULL)
184    return false;
185
186  if (this->fontTTF != NULL)
187  {
188    TTF_CloseFont(this->fontTTF);
189    this->fontTTF = NULL;
190  }
191  if (this->fastTextureID != 0)
192  {
193    if(glIsTexture(this->fastTextureID))
194      glDeleteTextures(1, &this->fastTextureID);
195    this->fastTextureID = 0;
196  }
197
198  this->fastTextureID = Text::loadTexture(surface, NULL);
199
200  // initializing the Glyphs.
201  if (this->glyphArray == NULL)
202  {
203    float cx,cy;
204    Glyph* tmpGlyph;
205    this->glyphArray = new Glyph*[FONT_HIGHEST_KNOWN_CHAR];
206    for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++)
207    {
208      tmpGlyph = this->glyphArray[i] = new Glyph;
209      cx=(float)(i%16)/16.0f;                  // X Position Of Current Character
210      cy=(float)(i/16)/16.0f;                  // Y Position Of Current Character
211
212      tmpGlyph->texCoord[0] = cx;
213      tmpGlyph->texCoord[1] = cx+0.0625f;
214      tmpGlyph->texCoord[2] = cy+0.001f;
215      tmpGlyph->texCoord[3] = cy+0.0625f;
216      tmpGlyph->width = 1;
217      tmpGlyph->advance = 1;
218      tmpGlyph->bearingX = 1;
219      tmpGlyph->bearingY = 1;
220      tmpGlyph->height = 1;
221    }
222  }
223  return true;
224}
225
226
227/**
228 *  sets a specific renderStyle
229 * @param renderStyle the Style to render: a string (char-array) containing:
230 *   i: italic, b: bold, u, underline
231 */
232void Font::setStyle(const char* renderStyle)
233{
234  this->renderStyle = TTF_STYLE_NORMAL;
235
236  for (int i = 0; i < strlen(renderStyle); i++)
237    if (strncmp(renderStyle+i, "b", 1) == 0)
238      this->renderStyle |= TTF_STYLE_BOLD;
239  else if (strncmp(renderStyle+i, "i", 1) == 0)
240    this->renderStyle |= TTF_STYLE_ITALIC;
241  else if (strncmp(renderStyle+i, "u", 1) == 0)
242    this->renderStyle |= TTF_STYLE_UNDERLINE;
243
244  if (likely(this->fontTTF != NULL))
245    TTF_SetFontStyle(this->fontTTF, this->renderStyle);
246//  else
247//    PRINTF(2)("Font was not initialized, please do so before setting the Font-Style.\n");
248}
249
250Font* Font::defaultFont = NULL;
251
252void Font::createAsciiImage(const char* fileName)
253{
254  if (this->fontTTF == NULL)
255    return;
256  int height = this->getMaxHeight();
257
258  //
259  SDL_Color tmpColor = {0, 0, 0};
260  // Surface definition.
261  SDL_Rect tmpRect; // this represents a Rectangle for blitting.
262  SDL_Surface* tmpSurf =  SDL_CreateRGBSurface(SDL_SWSURFACE,
263                                               height*16, height*16,
264                                               32,
265#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
266                                               0x000000FF,
267                                               0x0000FF00,
268                                               0x00FF0000,
269                                               0xFF000000
270#else
271                                               0xFF000000,
272                                               0x00FF0000,
273                                               0x0000FF00,
274                                               0x000000FF
275#endif
276                                              );
277  tmpRect.x = 0; tmpRect.y = 0; tmpRect.w = tmpSurf->w; tmpRect.h = tmpSurf->h;
278  SDL_SetClipRect(tmpSurf, &tmpRect);
279  int maxLineHeight = 0;
280
281  int posX, posY;
282  // all the interessting Glyphs
283  for (posY = 0; posY < 16; posY++)
284  {
285    for (posX = 0; posX < 16; posX++)
286    {
287      SDL_Surface* glyphSurf = NULL;
288      if (likely(this->fontTTF != NULL))
289      {
290        SDL_Color white = {255, 255, 255};
291        glyphSurf = TTF_RenderGlyph_Blended(this->fontTTF, posX+16*posY, white);
292      }
293      if( glyphSurf != NULL )
294      {
295        tmpRect.x = height*posX;
296        tmpRect.y = height*posY;
297        SDL_SetAlpha(glyphSurf, 0, 0);
298
299        SDL_BlitSurface(glyphSurf, NULL, tmpSurf, &tmpRect);
300        SDL_FreeSurface(glyphSurf);
301      }
302    }
303  }
304  SDL_SaveBMP(tmpSurf, fileName);
305  SDL_FreeSurface(tmpSurf);
306}
307
308/**
309 * initializes the default font
310 */
311void Font::initDefaultFont()
312{
313  if (Font::defaultFont == NULL)
314    Font::defaultFont = new Font(font_xpm);
315}
316
317/**
318 * deletes the default font
319 */
320void Font::removeDefaultFont()
321{
322  if (Font::defaultFont != NULL)
323    delete Font::defaultFont;
324  Font::defaultFont = NULL;
325}
326
327
328/**
329 * @returns the maximum height of the Font, if the font was initialized, 0 otherwise
330 */
331int Font::getMaxHeight()
332{
333  if (likely (this->fontTTF != NULL))
334    return TTF_FontHeight(this->fontTTF);
335  else
336    return 0;
337}
338
339/**
340 * @returns the maximum ascent of the Font, if the font was initialized, 0 otherwise
341
342   the ascent is the pixels of the font above the baseline
343 */
344int Font::getMaxAscent()
345{
346  if (likely(this->fontTTF != NULL))
347    return TTF_FontAscent(this->fontTTF);
348  else
349    return 0;
350}
351
352/**
353 * @returns the maximum descent of the Font, if the font was initialized, 0 otherwise
354
355   the descent is the pixels of the font below the baseline
356 */
357int Font::getMaxDescent()
358{
359  if (likely(this->fontTTF != NULL))
360    return TTF_FontDescent(this->fontTTF);
361  else
362    return 0;
363}
364
365/**
366 * @param character The character to get info about.
367 * @returns a Glyph struct of a character. This Glyph is a pointer,
368   and MUST be deleted by the user..
369
370   This only works for horizontal fonts. see
371   http://freetype.sourceforge.net/freetype2/docs/tutorial/step2.html
372   for more info about vertical Fonts
373 */
374Glyph* Font::getGlyphMetrics(Uint16 character)
375{
376  Glyph* rg = new Glyph;
377  rg->character = character;
378  if (likely (this->fontTTF!= NULL))
379  {
380    int miX, maX, miY, maY, adv;
381    TTF_GlyphMetrics(this->fontTTF, rg->character,
382                     &miX, &maX,
383                     &miY, &maY,
384                     &adv);
385    rg->minX = (float)miX / (float)this->renderSize;
386    rg->maxX = (float)maX / (float)this->renderSize;
387    rg->minY = (float)miY / (float)this->renderSize;
388    rg->maxY = (float)maY / (float)this->renderSize;
389    rg->advance = (float)adv / (float)this->renderSize;
390  }
391  rg->height = rg->maxY - rg->minY;
392  rg->width = rg->maxX - rg->minX;
393  rg->bearingX = (rg->advance - rg->width) / 2;
394  rg->bearingY = rg->maxY;
395  return rg;
396}
397
398/**
399 * creates a Fast-Texture of this Font
400 */
401GLuint Font::createFastTexture()
402{
403  /* interesting GLYPHS:
404  *  32: space
405  *  33-47: Special Characters.
406  *  48-57: 0-9
407  *  58-63: some more special chars (minor)
408  *  65-90: A-Z
409  *  97-122: a-z
410  */
411  int numberOfGlyphs = 91;
412
413  this->initGlyphs(32, numberOfGlyphs);
414//  this->glyphArray[32]->width = .5f; //!< @todo find out the real size of a Space
415
416  int rectSize = this->findOptimalFastTextureSize();
417
418  // setting default values. (maybe not needed afterwards)
419  SDL_Color tmpColor;  tmpColor.r = tmpColor.g = tmpColor.b = 0;
420  // Surface definition.
421  SDL_Rect tmpRect; // this represents a Rectangle for blitting.
422  SDL_Surface* tmpSurf =  SDL_CreateRGBSurface(SDL_SWSURFACE,
423                                               rectSize, rectSize,
424                                               32,
425#if SDL_BYTEORDER == SDL_LIL_ENDIAN /* OpenGL RGBA masks */
426                                               0x000000FF,
427                                               0x0000FF00,
428                                               0x00FF0000,
429                                               0xFF000000
430#else
431                                               0xFF000000,
432                                               0x00FF0000,
433                                               0x0000FF00,
434                                               0x000000FF
435#endif
436                                              );
437  tmpRect.x = 0; tmpRect.y = 0; tmpRect.w = tmpSurf->w; tmpRect.h = tmpSurf->h;
438  SDL_SetClipRect(tmpSurf, &tmpRect);
439  int maxLineHeight = this->getMaxHeight();
440
441  // all the interessting Glyphs
442  for (int i = 0; i < 128; i++)
443  {
444    SDL_Surface* glyphSurf = NULL;
445    Glyph* tmpGlyph;
446
447    if (tmpGlyph = this->glyphArray[i])
448    {
449      if (tmpGlyph->height*this->renderSize > maxLineHeight)
450        maxLineHeight = (int)(tmpGlyph->height*this->renderSize);
451
452      if (tmpRect.x+tmpGlyph->advance*this->renderSize > tmpSurf->w)
453      {
454        tmpRect.x = 0;
455        tmpRect.y = tmpRect.y + maxLineHeight;
456      }
457      if (tmpRect.y + maxLineHeight > tmpSurf->h)
458      {
459        PRINTF(1)("Protection, so font cannot write over the boundraries (!!this should not heappen!!)\n");
460        break;
461      }
462          // reading in the new Glyph
463      if (likely(this->fontTTF != NULL))
464      {
465        SDL_Color white = {255, 255, 255};
466        glyphSurf = TTF_RenderGlyph_Blended(this->fontTTF, i, white);
467      }
468      if( glyphSurf != NULL )
469      {
470        SDL_SetAlpha(glyphSurf, 0, 0);
471        int tmp = tmpRect.y;
472        tmpRect.y += this->getMaxAscent()-(int)((float)tmpGlyph->bearingY*this->renderSize);
473        SDL_BlitSurface(glyphSurf, NULL, tmpSurf, &tmpRect);
474        tmpRect.y = tmp;
475
476        tmpGlyph->texCoord[0] = (float)(tmpRect.x)/(float)tmpSurf->w;
477        tmpGlyph->texCoord[1] = (float)(tmpRect.x + tmpGlyph->width*(float)this->renderSize)/(float)tmpSurf->w;
478        tmpGlyph->texCoord[2] = (float)(tmpRect.y)/(float)tmpSurf->w;
479        tmpGlyph->texCoord[3] = (float)(tmpRect.y+this->getMaxHeight())/(float)tmpSurf->w;
480        SDL_FreeSurface(glyphSurf);
481        tmpRect.x += (int)(tmpGlyph->advance * this->renderSize)+1;
482
483        /*
484        // Outputting Glyphs to BMP-files.
485        char outname[1024];
486        if (i < 10)
487        sprintf( outname, "%s-glyph-00%d.bmp", this->getName(), i );
488        else if (i <100)
489        sprintf( outname, "%s-glyph-0%d.bmp", this->getName(), i );
490        else
491        sprintf( outname, "%s-glyph-%d.bmp", this->getName(), i );
492        SDL_SaveBMP(tmpSurf, outname);*/
493      }
494    }
495  }
496  // outputting the GLYPH-table
497  //   char outName[1024];
498  //   sprintf( outName, "%s-glyphs.bmp", this->getName());
499  //   SDL_SaveBMP(tmpSurf, outName);
500
501  GLuint texture;
502  glGenTextures(1, &texture);
503  glBindTexture(GL_TEXTURE_2D, texture);
504  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
505  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
506  glTexImage2D(GL_TEXTURE_2D,
507               0,
508               GL_RGBA,
509               tmpSurf->w, tmpSurf->h,
510               0,
511               GL_RGBA,
512               GL_UNSIGNED_BYTE,
513               tmpSurf->pixels);
514  SDL_FreeSurface(tmpSurf);
515  return texture;
516}
517
518/**
519 *  stores Glyph Metrics in an Array.
520 * @param from The Glyph to start from.
521 * @param count The number of Glyphs to start From.
522 */
523void Font::initGlyphs(Uint16 from, Uint16 count)
524{
525  /* initialize the Array, and set all its entries to NULL
526  *  only if the Glyph-array has not been initialized
527  */
528  if (!this->glyphArray)
529  {
530    this->glyphArray = new Glyph*[FONT_HIGHEST_KNOWN_CHAR];
531    for (int i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++)
532      this->glyphArray[i] = NULL;
533  }
534
535  Uint16 lastGlyph = from + count;
536
537  for (int i = from; i <= lastGlyph; i++)
538  {
539      // setting up all the Glyphs we like.
540    glyphArray[i] = getGlyphMetrics(i);
541  }
542  return;
543}
544
545/**
546 * @returns the optimal size to use as the texture size
547
548   @todo: this algorithm can be a lot faster, althought it does
549   not really matter within the init-context, and 128 glyphs.
550
551   This function searches for a 2^n sizes texture-size, this is for
552   openGL-version < 1.2 compatibility ( and because it is realy easy like this :))
553 */
554int Font::findOptimalFastTextureSize()
555{
556  if (this->glyphArray == NULL)
557    return 0;
558
559  int i;
560  int x,y; // the counters
561  int maxLineHeight = this->getMaxHeight();
562  unsigned int size = 32;  // starting Value, we have to start somewhere 32 seems reasonable. (take any small enough 2^i number)
563  bool sizeOK = false;
564  Glyph* tmpGlyph;
565
566  while (!sizeOK)
567  {
568    x = 0; y = 0;
569    for (i = 0; i < FONT_HIGHEST_KNOWN_CHAR; i++)
570    {
571      if((tmpGlyph = this->glyphArray[i]) != NULL)
572      {
573              // getting the height of the highest Glyph in the Line.
574        if (tmpGlyph->height*this->renderSize > maxLineHeight)
575          maxLineHeight = (int)(tmpGlyph->height*this->renderSize);
576
577        if (x + tmpGlyph->advance*this->renderSize > size)
578        {
579          x = 0;
580          y = y + maxLineHeight;
581                  //maxLineHeight = 0;
582        }
583        if (y + maxLineHeight + 1 > size)
584          break;
585        x += (int)(tmpGlyph->advance*this->renderSize)+1;
586
587      }
588    }
589    if (i >= FONT_HIGHEST_KNOWN_CHAR-1 || size > 8192)
590      sizeOK = true;
591    else
592      size *= 2;
593  }
594  return size;
595}
596
597
598/**
599 *  a simple function to get some interesting information about this class
600 */
601void Font::debug()
602{
603  // print the loaded font's style
604  int style;
605  if (likely(this->fontTTF != NULL))
606    style = TTF_GetFontStyle(this->fontTTF);
607  PRINTF(0)("The font style is:");
608  if(style==TTF_STYLE_NORMAL)
609    PRINTF(0)(" normal");
610  else {
611    if(style&TTF_STYLE_BOLD)
612      PRINTF(0)(" bold");
613    if(style&TTF_STYLE_ITALIC)
614      PRINTF(0)(" italic");
615    if(style&TTF_STYLE_UNDERLINE)
616      PRINTF(0)(" underline");
617  }
618  PRINTF(0)("\n");
619}
Note: See TracBrowser for help on using the repository browser.