Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 8763 was 8763, checked in by bensch, 18 years ago

minor nicer design

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