Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 7384 was 7221, checked in by bensch, 19 years ago

orxonox/trunk: merged the std-branche back, it runs on windows and Linux

svn merge https://svn.orxonox.net/orxonox/branches/std . -r7202:HEAD

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