Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/fontdata/src/lib/graphics/text_engine/font.cc @ 8754

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

better, but still not running, yet

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