Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8372 was 8316, checked in by bensch, 18 years ago

trunk: fixed most -Wall warnings… but there are still many missing :/

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