Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: less fileSys-stuff

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