Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/powerups/src/lib/graphics/text_engine/font.cc @ 5913

Last change on this file since 5913 was 5768, checked in by bensch, 19 years ago

orxonox/trunk: font is a Texture now (this is a procedural texture)

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