Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5712 was 5427, checked in by bensch, 19 years ago

orxonox/trunk: some minor include stuff

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