Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

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

orxonox/trunk: split open the text-engine

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