SDL Font Class problem

Started by
3 comments, last by Teenage Death Boy 19 years, 2 months ago
Ok so after using the font tutorial I found on Cone3d, I decided to try and make it into its own font class that I could plug into my engine (completed Pong, Tetris, Space Invaders, working on a 2d side scroller at the moment). I havn't really made a lot of chances but at the moment it looks like this. CFont header:

#ifndef __FONT_H__
#define __FONT_H__

#include "SDL.h"

class CFont
{
public:
	int				initFont(char *dir, float r, float g, float b, float a);
	void			drawString(SDL_Surface *dest, int x, int y, char *str, ...);
	int				stringWidth(char *str, ...);
	void			freeFont();
private:
	SDL_Surface		*m_font;
	int				m_width;		// width of sdl_surface image
	int				m_charWidth;	// width of one character (width/16)
	int 			*m_widths;		// real widths
	unsigned char	*m_data;		// raw font data

};

#endif


CFont source file



#ifndef __FONT_CPP__
#define __FONT_CPP__

#include "font.h"

#include <stdlib.h>
#include <stdarg.h>
#include <string.h>

/*
	Returns:	0 if successful, 1 if not
	Parameters:	Directory, RGB and alpha values
	Remarks:	None
*/
int CFont::initFont(char *dir, float r, float g, float b, float a)
{
  FILE *fp;                        
  char tempString[100];            
  unsigned char tmp;               
  int width;                       
  SDL_Surface *tempSurface;        

  // read font size
  sprintf(tempString,"%s/%s",dir,"font.ini");
  fp = fopen(tempString, "rb");
  if( fp == NULL )
  {
    return 1;
  }
  fscanf(fp, "%d", &width);
  fclose(fp);

  // create our font structure now
  m_data = new unsigned char[width*width*4];
  m_width = width;
  m_charWidth = width/16;

  // open the font raw data file and read everything in
  sprintf(tempString,"%s/%s",dir,"font.raw");
  fp = fopen(tempString, "rb");
  if( fp != NULL )
  {
    for(int i=0;i<width*width;i++)
    {
      tmp = fgetc(fp);
      m_data[i*4] = (unsigned char)255*(unsigned char)r;
      m_data[i*4+1] = (unsigned char)255*(unsigned char)g;
      m_data[i*4+2] = (unsigned char)255*(unsigned char)b;
      m_data[i*4+3] = (unsigned char)(((float)tmp)*a);
    }
  } else {
    return 0;
  }
  fclose(fp);
  // create surface for font
  Uint32 rmask,gmask,bmask,amask;
  #if SDL_BYTEORDER == SDL_BIG_ENDIAN
  rmask = 0xff000000;
  gmask = 0x00ff0000;
  bmask = 0x0000ff00;
  amask = 0x000000ff;
  #else
  rmask = 0x000000ff;
  gmask = 0x0000ff00;
  bmask = 0x00ff0000;
  amask = 0xff000000;
  #endif
  tempSurface = SDL_CreateRGBSurfaceFrom(m_data, width, width,
                              32, width*4, rmask, gmask, bmask, amask);
  m_font = SDL_DisplayFormatAlpha(tempSurface);
  SDL_FreeSurface(tempSurface);

  m_widths = new int[256];

  // now read in the information about the width of each character
  sprintf(tempString,"%s/%s",dir,"font.dat");
  fp = fopen(tempString, "rb");
  if( fp != NULL )
  {
    for(int i=0;i<256;i++)
    {
      tmp = fgetc(fp);
      m_widths=tmp;
    }
  }
  fclose(fp);
  return 0;
}

/*
	Returns:	None
	Parameters:	Destination surface, coords and the string
	Remarks:	None
*/
void CFont::drawString(SDL_Surface *dest, int x, int y, char *str, ...)
{
  char string[1024];                

  va_list ap;                   // Pointer To List Of Arguments
  va_start(ap, str);            // Parses The String For Variables
  vsprintf(string, str, ap);    // And Converts Symbols To Actual Numbers
  va_end(ap);                   // Results Are Stored In Text

  int length=strlen(string);             
  int xx=0;						// This will hold the place where to draw the next char
  for(int i=0; i<length; i++)   
  {
    // Draw single character
    SDL_Rect destrect;
	destrect.x = xx+x;
	destrect.y = y;

	SDL_Rect srcrect;
	srcrect.w = m_widths[string]+2;
	srcrect.h = m_charWidth;
	srcrect.x = (string%16*m_charWidth)+((m_charWidth/2)-(m_widths[string])/2);
	srcrect.y = (((int)string/16)*m_charWidth);

	SDL_BlitSurface(m_font, &srcrect, dest, &destrect);

    // Increase xx by the width of the character
    xx+=m_widths[string];
  }
}

/*
	Returns:	integer, length of string
	Parameters:	string to find width of
	Remarks:	None
*/
int CFont::stringWidth(char *str, ...)
{
  char string[1024];         

  va_list ap;                         // Pointer To List Of Arguments
  va_start(ap, str);                  // Parses The String For Variables
  vsprintf(string, str, ap);          // And Converts Symbols To Actual Numbers
  va_end(ap);                         // Results Are Stored In Text

  // Now we just count up the width of all the characters
  int xx = 0;
  int length = strlen(string);
  for(int i=0; i<length ;i++)
  {
    // Add their widths together
    xx += m_widths[string];
  }

  return xx;
}


/*
	Returns:	None
	Parameters:	None
	Remarks:	None
*/
void CFont::freeFont()
{
  delete [] m_widths;
  delete [] m_data;
  SDL_FreeSurface(m_font);
  delete m_font;
}

#endif



The way its implemented into the engine is; At the moment its a public member of my CGameEngine class (will be private soon). CFont* testfont; and in the constructor for my CGameEngine I run: testfont = new CFont; testfont->initFont("data/fonts/font1",1,1,1,1); as well as freeing it in the destructor testfont->freeFont(); delete testfont; This seems pretty straight forward. I just took the procedural code from the tutorial and encapsulated it in a class. The program breaks on run. So I used the debugger. Everything seems fine. It loads the width, opens the file etc. The problem gets when it reaches tempSurface = SDL_CreateRGBSurfaceFrom(m_data, width, width, 32, width*4, rmask, gmask, bmask, amask); m_font = SDL_DisplayFormatAlpha(tempSurface); Specifically, when it reaches the SDL_DisplayFormatAlpha command, though I'm figuring the problem is actually with the above line, and that is probably from the for loop that fills the m_data array. Is there anyone who could help me and point out where I went wrong? Any help is greatly appreciated EDIT: ok, the problem doesn't seem to come up if I declare the font as a global, only seems to happen when its a member of my game engine class.
Advertisement
You should just use SDL_TTF. I find Cone3D's solution to doing fonts and sprites horrible. I've done up my own SDL_TTF wrapper, here's an example of its use:
System::Text::LoadFont("Title", "data/bgothl.ttf",40);System::Text::LoadFont("Caption", "data/arial.ttf",13);System::Text::DefaultFont = "Caption";System::Text::Print(x, y, "Hello World", r, g, b);
It loads the font and names it Title, then loads arial.ttf and names it Caption and uses that as the default font. The Print function has an argument taking in what font to print with. I left it out though as it defaults to the default font. I plan on releasing the source to the new engine (codenamed Lucid Engine) "when it's complete". You can see its progress in my journal.

As an alternative, use SFont or BFont.

[Edited by - Rob Loach on February 9, 2005 9:24:02 AM]
Rob Loach [Website] [Projects] [Contact]
Ok thanks. If anyone can see the error, I still wouldn't mind knowing why it works as global but not as member to other class, in case I get a similar problem later
I haven't had time to look at everything, but here's one suggestion. This function is really dangerous!
void CFont::freeFont(){  delete [] m_widths;  delete [] m_data;  SDL_FreeSurface(m_font);  delete m_font;}

Here is a safer rewrite.
void CFont::freeFont(){   if(m_widths)   {     delete [] m_widths;     m_widths = 0;   }   if( m_data )   {     delete [] m_data;     m_data = 0;   }   if( m_font )   {     SDL_FreeSurface(m_font);     m_font = 0;   }  //delete m_font;  <--- big NO NO!}


One comment about pointers - you do not have to delete m_font because you never allocated memory for it. Anything that SDL used for it will be taken care of with the FreeSurface function. That could be what the problem was. Give that a try and see. If not then I'll take a look again later when I get back.

- Drew
hmm havn't had time to check it but I am pretty sure it won't fix the error. Stepping through each line, the problem comes during the initialisation, not in the destructor/free function.

When I get time tomorow I will try and make the changes and let you know. Thanks for the tips

This topic is closed to new replies.

Advertisement