Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: Preparing for FONT-DATA

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