Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8762 was 8761, checked in by bensch, 19 years ago

merged the new Font-Implementation back here
merged with svn merge https://svn.orxonox.net/orxonox/branches/fontdata . -r8752:HEAD
no conflicts, naturally

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