Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: some minor cleanup, of the mess i made with AutoMake-sh

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