Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: orxonox.OLD/branches/fontdata/src/lib/graphics/text_engine/font.cc @ 8754

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

better, but still not running, yet

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