Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 5943 was 5859, checked in by bensch, 19 years ago

orxonox/trunk: small changes in the Texture interface

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