Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

better with fonts

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