Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: rendering font with RenderSize, but not with TextSize. This makes the Font more controllable

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