Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: TextureToGL is now more modular

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