Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: added MultiLineText a Text for multiple line-input, that should automatically shilft position

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