[C] Simple SDL bitmap font

[C] Simple SDL bitmap font

Postby Pix3l » 01 Sep 2011, 21:35

Hello, i just wrote a simple example on how to use bitmap fonts with SDL in C.
I have just a little problem on make colors changes, when i specify a new color in Draw() function, seems it take only the first one.
Somebody can help with the issue?

I hope this snippets can be useful anyway :]

{l Code}: {l Select All Code}
// gcc -o bmpfont bmpfont.c -lSDL

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

SDL_Color white = {255, 255, 255};
SDL_Color cyan = {0, 255, 255};
SDL_Color orange = {255, 128, 0};

    //Global variables
    int t, tl = 0, frequency = 1000 / 100, temp;
    int repeat, i, quit=0;
    int playing = 1;
    SDL_Event event;
    SDL_Surface *screen, *textSurface;
    SDL_Rect rect; //Main drawing surface
    Uint8 *keystate; // keyboard state
    char text[20]; //Hold text

char message[35];

SDL_Surface *bmpfont;
SDL_Rect area;

Uint32 get_pixel(SDL_Surface *surface, int x, int y)
{
   int bpp = surface->format->BytesPerPixel;
   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
   
   switch (bpp)
   {
      case 1:
      return *p;

      case 2:
      return *(Uint16 *)p;

      case 3:
         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
         else
            return p[0] | p[1] << 8 | p[2] << 16;
      
      case 4:
         return *(Uint32 *)p;
      
      default:
         return 0;
   }
}

void put_pixel(SDL_Surface *_ima, int x, int y, Uint32 pixel)
{
   int bpp = _ima->format->BytesPerPixel;
   Uint8 *p = (Uint8 *)_ima->pixels + y * _ima->pitch + x*bpp;

   switch (bpp)
   {
      case 1:
         *p = pixel;
         break;
         
      case 2:
         *(Uint16 *)p = pixel;
         break;
         
      case 3:
         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
         {
            p[0]=(pixel >> 16) & 0xff;
            p[1]=(pixel >> 8) & 0xff;
            p[2]=pixel & 0xff;
         }
         else
         {
            p[0]=pixel & 0xff;
            p[1]=(pixel >> 8) & 0xff;
            p[2]=(pixel >> 16) & 0xff;
         }
         break;
         
      case 4:
         *(Uint32 *) p = pixel;
         break;
   }
}

/**
 * Rimpiazza un colore specifico (target) con uno un rimpiazzo (replecement)
 * in una zona ben precisa di una determinata superficie pixel per pixel
 *
 * @param SDL_Surface *src
 *      Puntatore alla superficie su cui agire
 * @param Uint32 target
 *      Colore RGB da sostituire
 * @param Uint32 replacement
 *      Colore RGB di rimpiazzo
 * @param SDL_Rect pick
 *      Regione della superifice su cui agire
 **/
void replaceColor (SDL_Surface * src, Uint32 target, Uint32 replacement, SDL_Rect pick)
{
   int x;
   int y;
   
   if (SDL_MUSTLOCK (src))
   {
      if (SDL_LockSurface (src) < 0)
         return;
   }
   
   for (x = pick.x; x < (pick.x+pick.w); x ++)
   {
       //~ printf("X %d\n", x);
      for (y = pick.y; y < (pick.y+pick.h); y ++)
      {
          //~ printf("Y %d\n", y);
         if (get_pixel (src, x, y) == target)
             put_pixel (src, x, y, replacement);
      }
   }

   if (SDL_MUSTLOCK (src))
      SDL_UnlockSurface (src);
}

/**
 * Copia una porzione dalla superficie bmpfont e la copia sulla superficie screen
 *
 * @param SDL_Surface *screen
 *      Puntatore alla superficie di destinazione
 * @param SDL_Surface *bmpfont
 *      Puntatore alla superficie di origine
 * @param int X, int Y
 *      Destinazione in cui si andra' a copiare il contenuto di origine
 * @param int w, int h
 *      Rispettivamente, larghezza e grandezza della superficie da copiare
 * @param int asciicode
 *      Singolo carattere convertito in un equivalente decimale
 * @param SDL_Color color
 *      Struttura contenente i valori RGB di colore da usare per la superficie
**/
void DrawChar(SDL_Surface *screen, SDL_Surface *bmpfont, int X, int Y, int w, int h, int asciicode, SDL_Color color)
{
     SDL_Rect pick;
     SDL_Surface temp;
     
     //Cordinate nella superficie con le lettere
     pick.x=(asciicode % 32)*w;
     pick.y=(asciicode / 32)*h;
     pick.w=w;
     pick.h=h;
     area.x=X;
     area.y=Y;
     area.w=w;
     area.h=h;
     
     replaceColor (bmpfont, SDL_MapRGB (bmpfont->format, 255, 255, 255),
                   SDL_MapRGB (bmpfont->format, color.r, color.g, color.b), pick);

     //int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
     SDL_BlitSurface(bmpfont,&pick,screen,&area);
}

void Drawstring(SDL_Surface *screen, int X, int Y, char text[], SDL_Color color) {
  int i=0;
  int asciicode;
  area.x=X;
  area.y=Y;

  //Per la lunghezza della stringa
  for (i=0;i<35;i++){
      asciicode=text[i]; //Valori decimali del carattere
      //~ printf("%d ascii\n", asciicode);
      if (asciicode == 0 ) {
                    break;
      }
      // 8x16 dimensioni della lettera
      DrawChar(screen, bmpfont, area.x, area.y, 8, 16, asciicode, color);   
      area.x=area.x+8;
      }
}

void DrawRect(int x, int y, int width, int height, int color)
 {
       rect.x = x;
      rect.y = y;
       rect.w = width;
       rect.h = height;
       SDL_FillRect(screen, &rect, color);
 }


void Draw()
{
    sprintf(message,"Example text! AAAABBBBBBCCCCCCCCC");   
   Drawstring(screen, 10, 10, message, cyan); //draw string with bmp font
   
    sprintf(message,"Example text!");   
   Drawstring(screen, 10, 40, message, orange); //draw string with bmp font
}

int fps_sync (void)
{

   t = SDL_GetTicks ();

   if (t - tl >= frequency)
   {
      temp = (t - tl) / frequency;
      tl += temp * frequency;
      return temp;
   }
   else
   {
      SDL_Delay (frequency - (t - tl));
      tl += frequency;
      return 1;
   }
}

int getInput()
{
 // Grab a keystate snapshot
 keystate = SDL_GetKeyState( NULL );
 /* Handle key presses */
}

int mainLoop()
 {
  //main game loop
  while(!quit)
   {
      SDL_PollEvent(&event);
   repeat = fps_sync ();
      for (i = 0; i < repeat; i ++)
       {
         if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))
          quit = 1; /* Window closed and game stopped */
           
           
       }
      
        // Cancella tutto lo schermo di gioco
      SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
      //draw to the screen
      Draw();
      // Aggiorna lo schermo di gioco
      SDL_Flip(screen);
   }
 }

int main()
{
  // init video stuff
   if(SDL_Init(SDL_INIT_VIDEO) != 0)
   {
      fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
      exit(-1);
   }
   atexit(SDL_Quit);

   // init screen
   screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_SWSURFACE);
   if(screen == NULL)
   {
      fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
      exit(-1);
   }


      bmpfont=SDL_LoadBMP("dosemu.bmp");
      if (bmpfont == NULL) {
       printf("Can't load font: %s\n", SDL_GetError()); //check stdout.txt for this
       exit(1);
      }
   
   //main game loop
   mainLoop();
   printf("Game has exit\n");
      SDL_Quit();
   return 0;
}


The font is here http://i56.tinypic.com/29z861.jpg
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Re: [C] Simple SDL bitmap font

Postby FreakNigh » 01 Sep 2011, 22:01

I've found that using SDL_GetRGBA() and SDL_FillRect() are quite sufficiently fast put/get pixel functions.

Also when using SDL, avoid modifying a surface if you can. SDL will store surfaces in hardware memory and blitting them will be as fast as opengl if you never modify them.
FreakNigh
 
Posts: 79
Joined: 23 Jun 2011, 08:45
Location: Philadelphia, USA

Re: [C] Simple SDL bitmap font

Postby Pix3l » 01 Sep 2011, 22:08

I get the problem, when i change font color pixel by pixel the first time, they remain modified and next time the function didn't find the white color they expect for changes...
I think i have to first copy a portion of the bmpfont surface on a temporary surface and make changes on it form let the original cleaned, or is best reload the image completly every time?
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Re: [C] Simple SDL bitmap font

Postby FreakNigh » 01 Sep 2011, 22:28

You can use the function SDL_DisplayFormatAlpha or SDL_DisplayFormat to copy a surface.

however it sounds slow to constantly be recoloring it... You probably want to return the surface that was created for the text too like sdl_ttf does because you'll probably be rendering it quite a few more times in your render loop.
FreakNigh
 
Posts: 79
Joined: 23 Jun 2011, 08:45
Location: Philadelphia, USA

Re: [C] Simple SDL bitmap font

Postby Pix3l » 02 Sep 2011, 13:11

I fix it, i made a small temporary surface for draw and eventually replace the color of one char a time.

{l Code}: {l Select All Code}
// gcc -o bmpfont bmpfont.c -lSDL

#include <stdio.h>
#include <stdlib.h>
#include <SDL/SDL.h>

#define SCREEN_WIDTH 320
#define SCREEN_HEIGHT 240

SDL_Color white = {255, 255, 255};
SDL_Color cyan = {0, 255, 255};
SDL_Color orange = {255, 128, 0};

    //Global variables
    int t, tl = 0, frequency = 1000 / 100, temp;
    int repeat, i, quit=0;
    int playing = 1;
    SDL_Event event;
    SDL_Surface *screen, *textSurface;
    SDL_Rect rect; //Main drawing surface
    Uint8 *keystate; // keyboard state
    char text[20]; //Hold text

char message[35];

SDL_Surface *bmpfont;
SDL_Rect area;

    //Temporary font surface
    SDL_Surface *tmp;

Uint32 get_pixel(SDL_Surface *surface, int x, int y)
{
   int bpp = surface->format->BytesPerPixel;
   Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
   
   switch (bpp)
   {
      case 1:
      return *p;

      case 2:
      return *(Uint16 *)p;

      case 3:
         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
            return p[0] << 16 | p[1] << 8 | p[2];
         else
            return p[0] | p[1] << 8 | p[2] << 16;
      
      case 4:
         return *(Uint32 *)p;
      
      default:
         return 0;
   }
}

void put_pixel(SDL_Surface *_ima, int x, int y, Uint32 pixel)
{
   int bpp = _ima->format->BytesPerPixel;
   Uint8 *p = (Uint8 *)_ima->pixels + y * _ima->pitch + x*bpp;

   switch (bpp)
   {
      case 1:
         *p = pixel;
         break;
         
      case 2:
         *(Uint16 *)p = pixel;
         break;
         
      case 3:
         if (SDL_BYTEORDER == SDL_BIG_ENDIAN)
         {
            p[0]=(pixel >> 16) & 0xff;
            p[1]=(pixel >> 8) & 0xff;
            p[2]=pixel & 0xff;
         }
         else
         {
            p[0]=pixel & 0xff;
            p[1]=(pixel >> 8) & 0xff;
            p[2]=(pixel >> 16) & 0xff;
         }
         break;
         
      case 4:
         *(Uint32 *) p = pixel;
         break;
   }
}

void replaceColor (SDL_Surface * src, Uint32 target, Uint32 replacement)
{
   int x;
   int y;
   
   if (SDL_MUSTLOCK (src))
   {
      if (SDL_LockSurface (src) < 0)
         return;
   }
   
   for (x = 0; x < src->w; x ++)
   {
       //~ printf("X %d\n", x);
      for (y = 0; y < src->h; y ++)
      {
          //~ printf("Y %d\n", y);
         if (get_pixel (src, x, y) == target)
             put_pixel (src, x, y, replacement);
      }
   }

   if (SDL_MUSTLOCK (src))
      SDL_UnlockSurface (src);
}

void DrawChar(SDL_Surface *screen, SDL_Surface *bmpfont, int X, int Y, int w, int h, int asciicode, SDL_Color color)
{
     SDL_Rect pick, tmprect;
     
     //Cordinate nella superficie con le lettere
     pick.x=(asciicode % 32)*w;
     pick.y=(asciicode / 32)*h;
     pick.w=w;
     pick.h=h;
     area.x=X;
     area.y=Y;
     area.w=w;
     area.h=h;
     
     tmprect.x=0;
     tmprect.y=0;
     tmprect.w=8;
     tmprect.h=16;
     
     SDL_BlitSurface(bmpfont,&pick,tmp,NULL);
     
     replaceColor (tmp, SDL_MapRGB (tmp->format, 255, 255, 255),
                   SDL_MapRGB (tmp->format, color.r, color.g, color.b));

     //int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);
     SDL_BlitSurface(tmp,NULL,screen,&area);
}

void Drawstring(SDL_Surface *screen, int X, int Y, char text[], SDL_Color color) {
  int i=0;
  int asciicode;
  area.x=X;
  area.y=Y;

  //Per la lunghezza della stringa
  for (i=0;i<35;i++){
      asciicode=text[i]; //Valori decimali del carattere
      //~ printf("%d ascii\n", asciicode);
      if (asciicode == 0 ) {
                    break;
      }
      // 8x16 dimensioni della lettera
      DrawChar(screen, bmpfont, area.x, area.y, 8, 16, asciicode, color);   
      area.x=area.x+8;
      }
}

void DrawRect(int x, int y, int width, int height, int color)
 {
       rect.x = x;
      rect.y = y;
       rect.w = width;
       rect.h = height;
       SDL_FillRect(screen, &rect, color);
 }


void Draw()
{
    sprintf(message,"Example text! AAAABBBBBBCCCCCCCCC");   
   Drawstring(screen, 10, 10, message, cyan); //draw string with bmp font
   
    sprintf(message,"Example text!");   
   Drawstring(screen, 10, 40, message, orange); //draw string with bmp font
   
    sprintf(message,"White!");   
   Drawstring(screen, 10, 60, message, white); //draw string with bmp font
}

int fps_sync (void)
{

   t = SDL_GetTicks ();

   if (t - tl >= frequency)
   {
      temp = (t - tl) / frequency;
      tl += temp * frequency;
      return temp;
   }
   else
   {
      SDL_Delay (frequency - (t - tl));
      tl += frequency;
      return 1;
   }
}

int getInput()
{
 // Grab a keystate snapshot
 keystate = SDL_GetKeyState( NULL );
 /* Handle key presses */
}

int mainLoop()
 {
  //main game loop
  while(!quit)
   {
      SDL_PollEvent(&event);
   repeat = fps_sync ();
      for (i = 0; i < repeat; i ++)
       {
         if (event.type == SDL_QUIT || (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE))
          quit = 1; /* Window closed and game stopped */
           
           
       }
      
        // Cancella tutto lo schermo di gioco
      SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0, 0, 0));
      //draw to the screen
      Draw();
      // Aggiorna lo schermo di gioco
      SDL_Flip(screen);
   }
 }

int main()
{
  // init video stuff
   if(SDL_Init(SDL_INIT_VIDEO) != 0)
   {
      fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
      exit(-1);
   }
   atexit(SDL_Quit);

   // init screen
   screen = SDL_SetVideoMode(SCREEN_WIDTH, SCREEN_HEIGHT, 32, SDL_SWSURFACE);
   if(screen == NULL)
   {
      fprintf(stderr, "Can't initialize SDL: %s\n", SDL_GetError());
      exit(-1);
   }


      bmpfont=SDL_LoadBMP("dosemu.bmp");
      if (bmpfont == NULL) {
       printf("Can't load font: %s\n", SDL_GetError()); //check stdout.txt for this
       exit(1);
      }

    tmp=SDL_CreateRGBSurface(SDL_SWSURFACE,8,16,32,0,0,0,0);
   
    if(tmp == NULL) {
        fprintf(stderr, "CreateRGBSurface failed: %s\n", SDL_GetError());
        exit(1);
    }
   
   //main game loop
   mainLoop();
   printf("Game has exit\n");
      SDL_Quit();
   return 0;
}


It's fast enough for this job.
I already tried SDL_ttf and in definitively works in the same way...
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Re: [C] Simple SDL bitmap font

Postby netpipe » 14 Jun 2020, 10:19

do you still have the font available ?
netpipe
 
Posts: 2
Joined: 09 Feb 2020, 09:21

Re: [C] Simple SDL bitmap font

Postby Pix3l » 16 Jun 2020, 20:47

netpipe {l Wrote}:do you still have the font available ?


Yes, it's now part of an open source project, RetroGear.
It's a simple bitmap font, nothing else...
http://www.pix3lworkshop.altervista.org/ - Your 8bit choice since 2006!
User avatar
Pix3l
 
Posts: 55
Joined: 10 Sep 2010, 21:00
Location: Italy

Who is online

Users browsing this forum: No registered users and 1 guest