Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Preparing for FONT-DATA

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