Pueden descargar el código fuente de esta lección.

Ahora que ya hemos conseguido mostrar una imagen en pantalla en la segunda parte de la lección 1, vamos a superponer varias imagenes de una manera más eficiente.



//Cabeceras
#include "SDL/SDL.h"
#include <string>

Estos son los archivos de cabecera para este programa.

Incluimos SDL.h porque obviamente necesitamos funciones de SDL.

//Atributos de la pantalla
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;

Aquí tenemos varios atributos de la pantalla.

Es fácil averiguar para que sirven estos atributos, SCREEN_WIDTH para el ancho de la ventana, y SCREEN_HEIGHT para el alto. SCREEN_BPP son los bits por píxel que tendrá la imagen. Las imágenes que usamos son todas de 32-bit.

//Superficies que vamos a usar
SDL_Surface *message = NULL;
SDL_Surface *background = NULL;
SDL_Surface *screen = NULL;

Estas son las tres imágenes que vamos a usar. background obviamente es la imagen que se verá de fondo, message es la imagen que dice Hello y screen es la ventana contenedora de las imágenes.

SDL_Surface *load_image( std::string filename )
{
    //Temporary storage for the image that's loaded
    SDL_Surface* loadedImage = NULL;

    //The optimized image that will be used
    SDL_Surface* optimizedImage = NULL;

Esta función es la encargada de cargar la imagen. Lo que hace es cargar la imagen y devolver un puntero a la versión optimizada de la imagen cargada. El argumento filename es la ruta de la imagen a cargar. loadedImage es la superfície que obtenemos cuando la imagen se carga. optimizedImage es la superfície que vamos a usar.

//Cargamos la imagen
    loadedImage = SDL_LoadBMP( filename.c_str() );

Lo primero es cargar la imagen usando SDL_LoadBMP(). Pero no se debe usar inmediatamente, ya que esta imagen es de 24-bit y screen es de 32-bit. No es recomendable fusionar imagenes con diferente formato porque SDL tendrá que cambiar el formato en el aire (Durante la ejecución del programa), ralentizándolo.

//Si nada va mal cargando la imagen
    if( loadedImage != NULL )
    {
        //Create an optimized image
        optimizedImage = SDL_DisplayFormat( loadedImage );

        //Free the old image
        SDL_FreeSurface( loadedImage );
    }

Lo siguiente es verificar que la imagen se ha cargado bien. Si ocurre algún error, loadedImage será NULL.

Si la imagen se carga correctamente, llamamos a SDL_DisplayFormat() para que cree una nueva versión de loadedImage en el mismo formato que screen. La razón por la que hacemos esto es porque cuando intentamos pegar una imagen en otra de diferente formato, SDL convierte la imagen, así que están en el mismo formato.

Al crear la imagen convertida ganamos en velocidad de ejecución, ya que convertimos la imagen cuando la cargamos, de este modo al aplicar la imagen a screen, ya está en el mismo formato. Por lo tanto SDL no tiene que convertirla en el aire.

Así que ahora tenemos dos imágenes, la imagen cargada antigua (loadedImage), y la nueva imagen optimizada (optimizedImage).

SDL_DisplayFormat() crea una imagen nueva optimizada pero no se deshace de la otra. Por eso hay que llamar a SDL_FreeSurface().

return optimizedImage;
}

A continuación, la nueva versión optimizada de la imagen se devuelve.

void apply_surface( int x, int y, SDL_Surface* source, SDL_Surface* destination )
{
    //Make a temporary rectangle to hold the offsets
    SDL_Rect offset;

    //Give the offsets to the rectangle
    offset.x = x;
    offset.y = y;

Aquí tenemos nuestra función para fusionar las imágenes.
Como argumentos tiene las coordenadas donde queremos fusionar la imagen y las dos imágenes.
Primero creamos un objeto de tipo SDL_Rect. Hacemos esto porque SDL_BlitSurface() solo acepta este tipo de dato.
SDL_Rect es un tipo de dato que representa un rectángulo. Tiene cuatro miembros representando los valores X e Y de un rectángulo (Ancho y alto).

//Fusión de la imagen
    SDL_BlitSurface( source, NULL, destination, &offset; );
}

Con esto vamos a fusionar las imágenes.

int main( int argc, char* args[] )
{

Empezamos con la función principal.

Cuando usamos SDL, siempre hay que usar la función main de esta manera:

int main( int argc, char* args[] )  
// o
int main( int argc, char** args ).
//Inicializar todos los subsistemas
    if( SDL_Init( SDL_INIT_EVERYTHING ) == -1 )
    {
        return 1;
    }

Usando SDL_Init() iniciamos SDL. A SDL_Init() le pasamos SDL_INIT_EVERYTHING, para que inicie cualquier subsistema de SDL. Los subsistemas SDL son cosas como video, audio, Temporizadores etc, que son componentes individuales usados para hacer juegos.

No vamos a usar todos subsistemas, pero no pasa nada si los inicializamos.

Si SDL no puede inicializarse, devuelve -1, en ese caso controlamos el error devolviendo 1, terminando el programa.

//Configuramos la pantalla
    screen = SDL_SetVideoMode( SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE );

Creamos una ventana, lo que nos devuelve un puntero a la misma. Así podremos aplicar las imágenes a la ventana.

Ya conocemos que son los tres primeros argumentos, el cuarto crea la ventana en memoria.

//Si ocurre algún error
    if( screen == NULL )
    {
        return 1;
    }

Si hay algún error al crear la ventana, screen será igual a NULL.

//Título de la ventana
    SDL_WM_SetCaption( "Hello World", NULL );

Fijamos el título de la ventana a Hello World. El segundo argumento es para indicar la ruta del icono de la ventana.

//Cargamos las imágenes
    message = load_image( "hello.bmp" );
    background = load_image( "background.bmp" );

Cargamos las imágenes usando la función que creamos anteriormente.

//Aplicamos el fondo a la ventana
    apply_surface( 0, 0, background, screen );

Aplicamos el fondo a la ventana con la función que hicimos. Antes de unir el fondo a la ventana, teníamos algo asi:

Pero al unirlas, tendremos algo así:

Cuando las unimos, se copian los píxels de una imagen a otra. Por eso el la imagen que estamos usando de fondo aparece en la esquina superior izquierda, queremos que el fondo ocupe toda la ventana, pero, ¿significa eso que tendremos que cargar la imagen de fondo 3 veces mas?

apply_surface( 320, 0, background, screen );
apply_surface( 0, 240, background, screen );
apply_surface( 320, 240, background, screen );

No, lo que hacemos es fusionar la misma imagen 3 veces mas.

//Aplicando el mensaje a la ventana
apply_surface( 180, 140, message, screen );

Ahora vamos a aplicar la imagen mensaje a la ventana, en las coordenadas X=180 y Y=140

El sistema de coordenadas de SDL no trabaja así:

Trabaja así:

El origen de coordenadas (0,0) está en la esquina superior izquierda. Por eso hay que aplicar la imagen de esta forma:

//Actualizando la pantalla
    if( SDL_Flip( screen ) == -1 )
    {
        return 1;
    }

Como en la lección anterior, hay que actualizar la patalla para ver las imágenes. Si ocurre algún error devuelve -1, y nosotros devolvemos 1.

//Esperamos 2 seg
    SDL_Delay( 2000 );

Llamamos a esta función para que la ventana se muestre durante 2 segundos en pantalla.

//Liberamos las imágenes
    SDL_FreeSurface( message );
    SDL_FreeSurface( background );

    //Quit SDL
    SDL_Quit();

    //Return
    return 0;
}

Ya que hemos terminado nuestro programa, usamos SDL_FreeSurface() para eliminar de memoria las variables que almacenaban las imágenes. Si no liberamos la memoria, estas variables se quedarán ocupando espacio.

Fuente:

lazyfoo.net

Siguiente tema: Programación para Juegos - Lección 3 Librerías de SDL