You are on page 1of 28

Departamento de Ingeniera e

Investigaciones Tecnolgicas

Sistemas Operativos
Jefe de Ctedra:
Autor:

Fabio E. Rivalta
Sabrina M. Alonso

Ao:

2012

Gua de uso de SDL

SDL Simple DirectMedia Library - es una API que posibilita al programador crear entornos que
incluyan video, audio, interfaz con el usuario y multithreading.
Ofrece muchas ventajas entre las cuales se pueden nombrar el funcionamiento en varias plataformas, la
facilidad de uso y la posibilidad de utilizar sus componentes por separado combinndolos con otras
bibliotecas.
Si bien est escrita en C, puede utilizarse con C++, C#, Java, PHP, Pascal entre otros.

Instalacin bajo Linux

La biblioteca puede bajarse de www.libsdl.org e instalarla manualmente. Sin embargo la forma ms


sencilla de instalar SDL en Linux es a travs de la terminal.
sudo apt-get install libsdl1.2-dev libsdl1.2debian

Ests dos bibliotecas incluyen las funciones bsicas. Sin embargo para obtener mayores funcionalidades es
conveniente instalar tambin las bibliotecas mencionadas a continuacin:
sudo apt-get libsdl-image1.2-dev libsdl-mixer1.2-dev

libsdl-image1.2-dev Nos permitir poder trabajar con imgenes en otro formato que
no
sea BMP.
libsdl-mixer1.2-dev - Sirve para trabajar con sonido.

Compilacin
Al igual que sucede con algunas bibliotecas como Ncurses es necesario sealar el uso de SDL al compilar.
Prueba: Prueba.o
g++ -o Prueba Prueba.o -lSDL
Prueba.o: Prueba.cpp
g++ -c Prueba.cpp

Si estamos utilizando bibliotecas adicionales tambin es necesario sealarlo.


Prueba: Prueba.o
g++ -o Prueba Prueba.o -lSDL_image
Prueba.o: Prueba.cpp
g++ -c Prueba.cpp

Programacin con SDL


Antes que nada para poder compilar con SDL debemos incluir la biblioteca que corresponda:
#include <SDL/SDL.h>

Si bien SDL est compuesta por varios subsistemas, en esta gua abarcaremos algunos temas.

Funciones generales

int SDL_Init (Uint32 Args)


Inicializa subsistemas de SDL. Es la primer funcin que debemos llamar al trabajar con SDL. Retorna un
valor menor a cero en caso de error, es decir si alguno de los subsistemas no pudiera ser inicializado.

int SDL_InitSubSystem (Uint32 Args)


Sirve para inicializar subsistemas que no hayamos indicado con SDL_Init. Retorna un valor menor a cero
en caso de error.

void SDL QuitSubSystem (Uint32 Args)


Cierra nicamente los subsistemas indicados y que hayan sido previamente inicializado con cualquiera de
las dos funciones que permiten realizar dicha accin.

Estas tres funciones reciben argumentos como


SDL_INIT_VIDEO
SDL_INIT_AUDIO
SDL_INIT_EVERYTHING

Pueden inicializarse o cerrarse varios subsistemas a la vez concatenando argumentos con |.


SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO)

void SDL_Quit (void)


Cierra todos los subsistemas de SDL y libera los recursos utilizados.

char * SDL_GetError (void)


Retorna una cadena que indica el error producido.

Video

Superficies:
El concepto ms importante a la hora de dibujar con SDL es el de superficie. Una superficie no es ms
que un rea de memoria que representa un conjunto de pixels que conforman un rectngulo. Posee un
ancho, alto y formato de cada uno de los pixels que la conforman.

SDL_Surface
Las superficies son representadas con una estructura compuesta de la siguiente manera:
typedef struct SDL_Surface
{
Uint32 flags;
SDL_PixelFormat *format;
int w, h;
Uint16 pitch;
void *pixels;
SDL_Rect clip_rect;
int refcount;
} SDL_Surface;

A continuacin detallaremos cada uno


Uint32 flags:
SDL_FULLSCREEN Nos permite indicar que la superficie ocupa toda la
pantalla
SDL_DOUBLEBUF Doble buffer(Explicaremos este tema un poco ms adelante)
SDL_RESIZABLE Indica que puede variarse el tamao de la superficie.

SDL_PixelFormat *format: Determina el formato de pixels que forman la superficie.


int w, h Valores de ancho y alto en pixels.

Uint16 pitch El ancho en pixels del framebuffer, es decir la cantidad de pixels


que se almacenan como si fueran una nica lnea, no siempre es igual que el ancho.

void *pixels Puntero a una cantidad n de pixels que conforman la superficie.

int refcount Cantidad referencias a la superficie.

Inicializacin de modo de video

SDL_Surface * SDL_SetVideoMode(int ancho, int alto, int bpp, Uint32 flags)


Antes de realizar cualquier otra accin debemos inicializar el modo de video deseado utilizando esta
funcin.
Retorna un puntero a la superficie inicializada o NULL en caso de error.
Ancho - X
Alto - Y
bpp- Bits por pixel. Profundidad de color, 8, 15, 16, 24, o 32, 0 si
queremos que SDL inicialice cualquier modo soportado
flags- SDL_FULLSCREEN Pantalla completa

SDL_DOUBLEBUF -

Doble buffer.

SDL_OPENGL Contextro OpenGL.

La superficie que reciba el valor de retorno de esta funcin ser para nosotros el framebuffer.

Dibujar
Una vez inicializado el modo de video podemos comenzar a dibujar la pantalla. Hay dos formas de trabajar
con superficies, variar pxel por pxel o utilizar imgenes.

1) Variar pixels:
Como bien sabemos la controladora de video de una computadora se encarga de plasmar en nuestra
pantalla la informacin contenida en un rea de memoria llamada framebuffer. Para visualizar este
concepto de forma sencilla podemos ver al framebuffer como un vector de n elementos en donde cada
uno de ellos representa un pxel en pantalla (es muy comn que realmente lo sea, en cuyo caso puede
nombrarse linear framebuffer). Al cambiar el valor de un elemento del framebuffer, cambiamos entonces
un pxel en pantalla.
Sin embargo cuando vemos una pantalla es ms fcil para nosotros asociarla a una matriz imaginaria por
ejemplo de 800 X 600 posiciones en donde el y,x - 0,0 representa la esquina superior izquierda y el y,x
599,799 la esquina inferior derecha.
La pregunta sera entonces: Si deseo cambiar el valor del pxel que se encuentra en y,x 50,400, Qu
posicin del vector debera modificar? Para acceder a dicho pxel necesitamos calcular el desplazamiento
dentro del vector utilizando la frmula pitch * Y + X.
No utilizamos w ya que no podemos estar seguros si el ancho de la superficie a dibujar es igual a la
cantidad de pixels del framebuffer.
Por otro lado para dar valor a un pxel debemos entender su formato, durante toda esta gua utilizaremos el
formato hicolor, en donde cada pxel se representa con 16 bits, normalmente se utilizan 5 para el rojo, 6
para el verde y 5 para el azul.
En SDL los pixels son representados por la siguiente estructura
typedef struct SDL_PixelFormat
{
SDL_Palette * palette;
Uint8 BitsPerPixel;
Uint8 BytesPerPixel;
Uint8 Rloss, Gloss, Bloss, Aloss;
Uint8 Rshift, Gshift, Bshift, Ashift;
Uint32 Rmask, Gmask, Bmask, Amask;
Uint32 colorkey;
Uint8 alpha;
} SDL_PixelFormat;

* palette Puntero a la superficie palette

BitsPerPixel Profundidad de color, los valores son 8,15,16,24,32

BytesPerPixel Nmero de bytes que necesita cada pxel.

Rloss, Gloss, Bloss, AlossUn color se obtiene mediante tres componentes: rojo,verde y azul. Estos tres
colores se asignan a un byte y dependiendo de la graduacin de los mismos el
resultado ser un determinado color.
Segn el modo de video se asignarn ms o menos bits a cada color. Por ejemplo un
modo de video 565 significa que se asignan 5 bits para el rojo, 6 para el verde y 5
para el azul.
Los campos Rloss, Gloss y Bloss indican cuantos bits de un byte se pierden al
asignar un valor a uno de estos colores. Por ejemplo en el formato anteriormente
mencionado el color rojo y azul perderan 3 bits y el verde 2.
Aloss sirve para indicar esto mismo pero para el valor alfa, es decir
transparencia.

Rshift, Gshift, Bshift, Ashift Cantidad de bits a desplazar el valor dado a un color para adaptarlo al formato
correcto.

Rmask, Gmask, Bmask, Amask Mscara de bits para extraer componentes rojo, verde,
azul y alfa segn corresponda.

Colorkey Color para realizar la operacin blitting de la cual hablaremos ms


adelante.

Alpha Transparecia.

Una vez que le hayamos dado valor a un pxel procederemos a colocarlo en el framebuffer.
Como el framebuffer un recurso crtico debemos utilizar algn mecanismo al estilo semforos. En este
caso SDL provee dos funciones que en realidad pueden ser utilizadas con cualquier superficie y no slo
con el framebuffer.

void * SDL_LockSurface (SDL_Surface *)


Bloquea una superficie para su uso. Anlogo a P en semforos. Retorna NULL en caso de falla.

void * SDL_UnlockSurface (SDL_Surface *)


Desbloquea una superficie previamente bloqueada con SDL_LockSurface. Anlogo a V en semforos.

Una vez que escribimos en el framebuffer debemos indicar que queremos plasmar estos cambios en la
pantalla. Para ello utilizamos la funcin:
void SDL_UpdateRect (SDL_Surface *, int lim_izq, int lim_sup, int lim_der, int lim_inf)
Actualiza una determinada zona de la superficie recibida por parmetro delimitada por
lim_izq - X de comienzo
lim_sup -Y de comienzo
lim_der - X de finalizacin
lim_inf - Y de finalizacin

Si en vez de colocar valores especficos a los lmites colocamos cero en todos, SDL actualizar toda la
superficie.
A continuacin veremos un ejemplo que integra todo lo visto hasta este punto
Ejemplo_1

#include <SDL/SDL.h>
#include <iostream>
#include <stdlib.h>
using namespace std;

Uint16 crearColor (SDL_PixelFormat * pf, Uint8 rojo, Uint8 verde, Uint8 azul)
{
Uint16 valorRGB;
valorRGB = ((rojo >> pf->Rloss) << pf->Rshift) +
((verde >> pf->Gloss) << pf->Gshift)+
((azul >> pf->Bloss) << pf->Bshift);
return valorRGB;
}

int main(int argc, char *argv[])


{
SDL_Surface * pantalla;
Uint16 * aux_pixels;
Uint16

color;

int offset, x, y;

if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"Error al inicializar"<< SDL_GetError()<<endl;
return 1;
}

/*De esta forma me aseguro que al cerrarse el programa se cierren tambin todos
los
subsitemas de SDL*/
atexit(SDL_Quit);

/*Inicializo el modo de video con una pantalla de 256 x 256 con 16 bytes de
profundidad de color */

pantalla = SDL_SetVideoMode(256, 256, 16, 0);


if (pantalla == NULL)
{
cout<<"Error modo de video"<< SDL_GetError()<<endl;
return 1;
}

//Bloque el uso de la superficie


SDL_LockSurface(pantalla);
aux_pixels = (Uint16 *) pantalla->pixels;

for (x = 0; x < 256; x++)


for (y = 0; y < 256; y++)
{
color = crearColor(pantalla->format,x, 100, y);
/* Notese que no utilizamos la dimensin x (el ancho de pantalla), para
calcular el offset correcto, sino que
utilizamos pitch, es decir la cantidad de
pixeles que se tratan como una sola lnea.
Podemos comprobar que a veces no es lo mismo que X. Coloque por ejemplo:
cout<<screen->pitch<<endl; y compare */

offset = (pantalla->pitch / 2 * y + x);


aux_pixels[offset] = color;
}

//Desbloqueamos la superficie
SDL_UnlockSurface(pantalla);

//Actualizamos toda la superficie


SDL_UpdateRect(pantalla, 0, 0, 0, 0);

//Realizamos una pausa para ver el resultado


SDL_Delay(3000);
return 0;
}

2) Trabajar con imgenes:


Es muy normal que para facilitar la tarea en vez de dibujar pxel por pxel optemos por utilizar imgenes.
La biblioteca bsica de SDL permite utilizar imgenes BMP exclusivamente, para utilizar imgenes en otro
formato debemos adems incluir la biblioteca SDL_Image.h.
#include <SDL/SDL_image.h>

Recuerde que la misma no forma parte de las bibliotecas bsicas de SDL y por lo tanto debe ser instalada
a parte as como tambin indicada al momento de compilar.

SDL_Surface *SDL_LoadBMP(const char * archivo.BMP);


Permite cargar la imagen BMP pasada por argumento a una determinada superficie. Retorna NULL si la
imagen no pudo ser cargada.

SDL_Surface * IMG_Load(const char * archivo)


Permite cargar otros formatos de imagen como png, jpg, gif. Retorna NULL si al imagen no pudo ser
cargada.
La operacin ms importante al trabajar con imgenes recibe el nombre de BLIT ( Block Image Transfer).
Esta operacin nos permite copiar una superficie a otra idntica. Es decir que podemos reproducir una
imagen cargada en una superficie_x en otra superficie_y sin tener que copiar pxel por pxel.
Por ejemplo para SDL el framebuffer es una superficie por lo tanto podemos dibujar en toda la pantalla
con una nica operacin.

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)


Permite realizar copiar una superficie parcial o totalmente a otra superficie idntica. Retorna un valor
menor a cero en caso de error o cero en caso de xito.
SDL_Surface * src Puntero a la superficie origen.
SDL_Rect * srcrect Regin de la superficie origen que debe ser copiada. Si colocamos NULL se
copiar la totalidad de la superficie origen.
SDL_Surface * dst Superficie destino
SDL_Rect * dstrect Regin destino. No importa el ancho y el alto slo la posicin x,y
SDL_Rect: Esta estructura nos permite definir un rea rectangular.
typedef struct SDL_Rect
{
Sint16 x, y; //Coordenada de inicio
Uint16 w, h; //ancho y alto a copiar partiendo de coordenada de inicio
} SDL_Rect;

Como las superficies se alocan dinmicamente al ser cargadas antes de cerrar nuestro programa o cuando
no nos interesa ms utilizar una determinada superficie debemos liberar memoria.

void SDL_FreeSurface(SDL_Surface* superficie);


Libera la memoria ocupada por una determinada imagen.

Ejemplo carga Imagen BMP


Ejemplo_2

#include <SDL/SDL.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
int main()
{
SDL_Surface *screen;
SDL_Surface *image;
SDL_Rect src, dest;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"No es posible iniciar SDL"<< SDL_GetError()<<endl;
return 1;
}
atexit(SDL_Quit);
/* Cargamos la imagen deseada */
image = SDL_LoadBMP("mariposa.bmp");
if (image == NULL)
{
cout<<"No se ha podido cargar el archivo"<<endl;
return 1;
}
//Ajusto el tamao de pantalla al tamao de la imagen
screen = SDL_SetVideoMode(image->w, image->h, 16, 0);
if (screen == NULL)
{
cout<<"No se ha podido configurar el modo de video deseado."<<
SDL_GetError()<<endl;
return 1;
}
/* Ahora utilizo las estructuras SDL_Rect para sealar la zona de la imagen que
utilizar para la funcin BLIT
En este caso copiare desde la coordenada 0,0 de la imagen, el ancho y alto
de la misma, es decir copiare toda la imagen.*/
src.x = 0;
src.y = 0;
src.w = image->w;
src.h = image->h;

dest.x
dest.y
dest.w
dest.h

=
=
=
=

0;
0;
image->w;
image->h;

SDL_BlitSurface(image, &src, screen, &dest);


SDL_UpdateRect(screen, 0, 0, 0, 0);
SDL_Delay(3000);
/* Libero memoria */
SDL_FreeSurface(image);
return 0;
}

Es muy comn que al realizar juegos tengamos que utilizar muchas imgenes una sobre la otra, por
ejemplo piezas sobre un tablero. En este caso deberamos dibujar nicamente la imagen pieza y no su
fondo.
SDL permite definir un valor llamado colorkey.

int SDL_SetColorKey(SDL_Surface *superfice, Uint32 flag, Uint32 colorkey);


Permite definir un color que al dibujar se tratar como si fuera transparente y por lo tanto no se dibujara al
realizar la operacin BLIT. Extrae el color pasado por parmetro.
superficie superficie a la cual extraer el color
flag - En este caso siempre utilizaremos SDL_SRCCOLORKEY
key definicin del color a extraer
Para definir el color utilizaremos otra funcin:
Uint32 SDL_MapRGB(SDL_PixelFormat *formato, Uint8 r, Uint8 g, Uint8 b)
Nos permite definir un color y adaptarlo a determinado formato de pxel.
Formato formato de pixelFormat
r,g,b valores de 0 255 que conforman la cantidad de rojo, verde y azul que componen a un
color.

Para clarificar este tema veamos un ejemplo.


Ejemplo_3

Sin extraccin de color de fondo

Con extraccin de color de fondo

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <iostream>
#include <stdlib.h>
using namespace std;
int main(int argc, char *argv[])
{
SDL_Surface *pantalla;
SDL_Surface *fondo;
SDL_Rect src, dest;
SDL_Surface *imagen;
Uint32 colorkey;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"Error al inicializar SDL"<<endl;
return 1;
}
atexit(SDL_Quit);
fondo = IMG_Load ("fondo.JPG");
if (fondo == NULL)
{
cout<<"No se ha podido cargar la imagen de fondo"<<endl;
return 1;
}
//Ajusto el tamao de pantalla al tamao de la imagen fondo
pantalla = SDL_SetVideoMode(fondo->w, fondo->h, 16, 0);
if (pantalla == NULL)
{
cout<<"No es posible establecer el modo de video "<<endl;
return 1;
}
//Cargo una imagen
imagen = IMG_Load("Dibujo.PNG");
if (imagen == NULL)
{
cout<<"No se puede cargar la imagen";
return 1;
}

/* Dibujamos el fondo */
src.x = 0;
src.y = 0;
src.w = fondo->w;
src.h = fondo->h;
dest.x = 0;
dest.y = 0;
dest.w = fondo->w;
dest.h = fondo->h;
SDL_BlitSurface(fondo, &src, pantalla, &dest);
//El color de fondo es verde puro es decir r,g,b - 0.255.0
colorkey = SDL_MapRGB(imagen->format, 0, 255, 0);
SDL_SetColorKey(imagen, SDL_SRCCOLORKEY, colorkey);
src.x = 0;
src.y = 0;
src.w = imagen->w;
src.h = imagen->h;
dest.x = 100;
dest.y = 90;
dest.w = imagen->w;
dest.h = imagen->h;

SDL_BlitSurface(imagen, &src, pantalla, &dest);


SDL_UpdateRect(pantalla, 0, 0, 0, 0);
SDL_Delay(3000);
SDL_FreeSurface(imagen);
SDL_FreeSurface(fondo);
return 0;
}

Alpha
El valor alpha permite establecer opacidad en imgenes. Cuanto mayor sea ms opaca la imagen ser.
int SDL_SetAlpha(SDL_Surface *surface, Uint32 flags, Uint8 alpha)
Permite establecer el nivel de opacidad de la imagen.
Superficie superficie a la que se le aplicar este efecto.
Flags en este caso colocaremos SDL_SRCALPHA
alpha - valor de 0 255 siendo 0 absolutamente transparente.
Veamos el mismo ejemplo anterior pero dibujaremos la imagen superior con una transparencia del 50 %
(128)
Ejemplo_4

#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <iostream>
#include <stdlib.h>
using namespace std;

int main(int argc, char *argv[])


{
SDL_Surface *pantalla;
SDL_Surface *fondo;
SDL_Rect src, dest;
SDL_Surface *imagen;
Uint32 colorkey;

if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"No se ha podido inciailizar SDL"<< SDL_GetError()<<endl;
return 1;
}
atexit(SDL_Quit);

fondo = IMG_Load ("fondo.JPG");


if (fondo == NULL)
{
cout<<"No se ha podido cargar la imagen de fondo"<<endl;
return 1;
}

//Ajusto el tamao de pantalla al tamao de la imagen


pantalla = SDL_SetVideoMode(fondo->w, fondo->h, 16, 0);
if (pantalla == NULL)
{
cout<<"No se ha podido inicializar el modo de video deseado."<<
SDL_GetError()<<endl;
return 1;
}

imagen = IMG_Load("Dibujo.PNG");
if (imagen == NULL)
{
cout<<"No ha podido cargar la imagen";
return 1;
}

/* Dibujamos fondo. */
src.x = 0;
src.y = 0;
src.w = fondo->w;
src.h = fondo->h;
dest.x = 0;
dest.y = 0;
dest.w = fondo->w;
dest.h = fondo->h;
SDL_BlitSurface(fondo, &src, pantalla, &dest);

colorkey = SDL_MapRGB(imagen->format, 0, 255, 0);


SDL_SetColorKey(imagen, SDL_SRCCOLORKEY, colorkey);

//Seteamos el valor de alpha deseado


SDL_SetAlpha(imagen, SDL_SRCALPHA, 128);
src.x = 0;
src.y = 0;
src.w = imagen->w;
src.h = imagen->h;
dest.x =

100;

dest.y = 90;
dest.w = imagen->w;
dest.h = imagen->h;

SDL_BlitSurface(imagen, &src, pantalla, &dest);


SDL_UpdateRect(pantalla, 0, 0, 0, 0);
SDL_Delay(3000);
SDL_FreeSurface(imagen);
SDL_FreeSurface(fondo);
return 0;
}

Optimizacin
Una animacin no es ms que una secuencia de imgenes estticas que se suceden una detrs de la otra
rpidamente. Cuando estamos trabajando con muchas imgenes y las estamos moviendo repetidamente
podemos observar que nuestro programa se vuelve lento.
Pueden puede deberse a dos problemas:
1) Si estamos dibujando directamente en el framebuffer puede que la imagen se refresque mientras estamos
moviendo alguno de los elementos con lo cual este se dibujar parcialmente o no se dibujara.
Para solucionar este problema utilizaremos un doblebuffer. El doblebuffer no es ms que un rea
intermedia en memoria en donde colocaremos todo lo que queremos dibujar. Una vez que hayamos
compuesto nuestra pantalla en este buffer de video virtual procederemos a copiar su contenido al buffer
de video real.
Para utilizar el doblebuffer debemos tener en cuenta dos cosas:
Debemos indicarlo al inicializar modo de video
SDL_SetVideoMode(512, 320, 16, SDL_DOUBLEBUF);
Ya no utilizaremos SDL_UpdateRect, en su lugar utilizaremos
int SDL_Flip(SDL_Surface* superficie)
2) Los pixels que componen la imagen que estamos dibujando son de formato diferente a los de la pantalla.
Para solucionar este problema utilizaremos
SDL_Surface *SDL_DisplayFormat(SDL_Surface *surface);
Esta funcin permite convertir el formato de los pixels de una superficie al mismo formato de los
pixels de la pantalla.
A continuacin veremos dos ejemplos, en el primero no utilizaremos ninguna de las soluciones vistas, en el
segundo utilizaremos conversin de pixels y doblebuffer. Compare la velocidad de ejecucin.

Sin doblebuffer ni conversin de formatos


Ejemplo_5
#include <cstdlib>
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <vector>

using namespace std;

int main(int argc, char *argv[])


{
SDL_Surface *fondo;
SDL_Surface *pantalla;
SDL_Surface *imagen;
SDL_Rect src, dest;
int i,j;
vector <pair<int,int> > elementos;

if (SDL_Init(SDL_INIT_VIDEO) != 0)
atexit(SDL_Quit);

//Inicializamos sin doblebuffer


pantalla = SDL_SetVideoMode(512, 320, 16, 0);
if (pantalla == NULL)
{
cout<<"No es posible inicializar el modo de video solicitado";
return 1;
}

/*Se carga la imagen deseada y se la convierte al formato de pixel de pantalla


de esta forma se evitara la conversin cada vez que hagamos la operacin BLIT*/
fondo = IMG_Load("fondo.JPG");
if (fondo == NULL)
{
cout<<"No se ha podido cargar la imagen";
return 1;
}

/*Cargo la imagen que dibujare sobre le fondo de la misma forma que la


anterior*/
imagen = IMG_Load("Dibujo.PNG");
if (imagen == NULL)
{
cout<<"No se ha podido cargar la imagen";
return 1;
}
/* quito el color de fondo de mi imagen*/
SDL_SetColorKey(imagen, SDL_SRCCOLORKEY | SDL_RLEACCEL,
(Uint16)SDL_MapRGB(imagen->format, 0, 255, 0));
/*Convierto el formato de pixel al igual que lo hice con la imagen anterior*/

//Ahora colocare 50 imagenes en diferentes posiciones


//Este vector contiene un par que representar mi x.y para un elemento i
elementos.resize(100);
for(i=0;i<100;i++)
{
elementos[i].first = rand() % pantalla->w;
elementos[i].second = rand() % pantalla->h;
}

for (j = 0; j < 100; j++)


{
/*Note que siempre comienzo por dibujar el fondo, esto se debe a que las imgens
se colocan "una arriba de otra", es decir que si no dibujo el fondo nuevamente
*/
src.x = 0;
src.y = 0;
src.w = fondo->w;
src.h = fondo->h;
dest = src;
SDL_BlitSurface(fondo, &src, pantalla, &dest);

for (i = 0; i < 100; i++)


{
src.x = 0;
src.y = 0;
src.w = imagen->w;
src.h = imagen->h;

dest.x = elementos[i].first - imagen->w / 2;

dest.y = elementos[i].second - imagen->h / 2;


dest.w = imagen->w;
dest.h = imagen->h;

SDL_BlitSurface(imagen, &src, pantalla, &dest);


}
//Coloco el contenido del buffer virtual en la pantalla
SDL_UpdateRect(pantalla,0,0,0,0);
SDL_Delay(30);
for(i=0;i<100;i++)
{
elementos[i].first = rand() % pantalla->w;
elementos[i].second = rand() % pantalla->h;
}
}

SDL_FreeSurface(fondo);
SDL_FreeSurface(imagen);
return 0;
}

Utilizando conversin y doblebuffer


Ejemplo_5bis
#include <cstdlib>
#include <iostream>
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <vector>

using namespace std;

int main(int argc, char *argv[])


{
SDL_Surface *aux;
SDL_Surface *fondo;
SDL_Surface *pantalla;
SDL_Surface *imagen;
SDL_Rect src, dest;
int i,j;
vector <pair<int,int> > elementos;

if (SDL_Init(SDL_INIT_VIDEO) != 0)
atexit(SDL_Quit);

//Inicializamos con doblebuffer


pantalla = SDL_SetVideoMode(512, 320, 16, SDL_DOUBLEBUF);
if (pantalla == NULL)
{
cout<<"No es posible inicializar el modo de video solicitado";
return 1;
}

/*Se carga la imagen deseada y se la convierte al formato de pixel de pantalla


de esta forma se evitara la conversin cada vez que hagamos la operacin BLIT*/
aux = IMG_Load("fondo.JPG");
if (aux == NULL)
{
cout<<"No se ha podido cargar la imagen";
return 1;
}
fondo = SDL_DisplayFormat(aux);
if (fondo == NULL)
{
cout<<"No se ha podido realizar la conversin";
return 1;
}
SDL_FreeSurface(aux);

/*Cargo la imagen que dibujare sobre le fondo de la misma forma que la


anterior*/
aux = IMG_Load("Dibujo.PNG");
if (aux == NULL)
{
cout<<"No se ha podido cargar la imagen";
return 1;
}
/* quito el color de fondo de mi imagen*/
SDL_SetColorKey(aux, SDL_SRCCOLORKEY | SDL_RLEACCEL, (Uint16)SDL_MapRGB(aux>format, 0, 255, 0));
/*Convierto el formato de pxel al igual que lo hice con la imagen anterior*/
imagen = SDL_DisplayFormat(aux);
if (imagen == NULL)

{
cout<<"No se ha podido realizar la conversin";
return 1;
}
SDL_FreeSurface(aux);

//Ahora colocare 50 imgenes en diferentes posiciones


//Este vector contiene un par que representar mi x.y para un elemento i
elementos.resize(100);
for(i=0;i<100;i++)
{
elementos[i].first = rand() % pantalla->w;
elementos[i].second = rand() % pantalla->h;
}

for (j = 0; j < 100; j++)


{
/*Note que siempre comienzo por dibujar el fondo, esto se debe a que las
imgenes
se colocan "una arriba de otra", es decir que si no dibujo el fondo nuevamente
*/
src.x = 0;
src.y = 0;
src.w = fondo->w;
src.h = fondo->h;
dest = src;
SDL_BlitSurface(fondo, &src, pantalla, &dest);

for (i = 0; i < 100; i++)


{
src.x = 0;
src.y = 0;
src.w = imagen->w;
src.h = imagen->h;

dest.x = elementos[i].first - imagen->w / 2;


dest.y = elementos[i].second - imagen->h / 2;
dest.w = imagen->w;
dest.h = imagen->h;

SDL_BlitSurface(imagen, &src, pantalla, &dest);


}

//Coloco el contenido del buffer virtual en la pantalla


SDL_Flip(pantalla);
SDL_Delay(30);
for(i=0;i<100;i++)
{
elementos[i].first = rand() % pantalla->w;
elementos[i].second = rand() % pantalla->h;
}
}

SDL_FreeSurface(fondo);
SDL_FreeSurface(imagen);
return 0;
}

IMPORTANTE:
Posicionar imgenes
Note la forma en que se posiciona la imagen
dest.x = elementos[i].first - imagen->w / 2;
dest.y = elementos[i].second - imagen->h / 2;

SDL dibuja desde el medio de la imagen, por lo tanto si nosotros queremos que la imagen est en por
ejemplo la posicin 100,100 deberamos colocar
dest.x = 100 - imagen->w / 2;
dest.y = 100 - imagen->h / 2;

En lugar de dest.x=100 y dest.y=100

Texto en SDL
Si bien hay bibliotecas para el manejo de texto que pueden descargarse, veremos como hacerlo a partir de
una imagen.

La lgica a seguir es:

1. Cargar una imagen que contenga todas las letras y smbolos que deseamos poner por pantalla.
SDL_Surface * imgletras=NULL;
imgletras=IMG_Load("Letras.PNG");
if(!imgfondo)
{
cout<<"Error al cargar imagen de letras"<<endl;
exit(1);
}

Por ejemplo, nuestro archivo Letras.PNG contiene

2. Extraer el color de fondo de dicha imagen (En este caso rgb - 0,255,0, es decir verde puro)
Uint32 colorkey = SDL_MapRGB(imgletras->format, 0, 255, 0);
SDL_SetColorKey(imgletras, SDL_SRCCOLORKEY, colorkey);

3. Por cada letra que deseamos escribir calcular el desplazamiento sobre la superficie que contiene
letras y cortar el sector que corresponda. Para realizar esto ltimo necesitamos conocer el ancho
y alto de la letra (24X24 en nuestro ejemplo).
El siguiente fragmento de cdigo muestra como escribir la letra E. Note que para realizar este paso en
apariencia complicado utilizaremos dos estrucutras SDL_Rect y una operacin SDL_BlitSurface al igual
que hacemos para colocar cualquier otra imagen.
/*En ancho y alto de SDL_Rect de origen colocamos los valores
correspondientes al
cuadrado que deseamos cortar. No el ancho y alto de la
imagen Letras.PNG*/

SDL_Rect src,dest;
src.w= 24;
src.h= 24;

/*Indicamos la coordenada x,y de la imagen Letras.PNG a partir de la cual


deseamos comenzar a dibujar, es decir a partir de donde se cortar un
cuadrado de src.w, src.h pixels */

src.x= 24* 4;
src.y= 0;

/*Posicionamos la imagen teniendo las mismas consideraciones que con


cualquier otra
imagen*/
dest.x = 100- src.w / 2;
dest.y = 100 - src.h / 2;
dest.w = src.w;
dest.h = src.h;

//Realizamos la operacin Blit.


SDL_BlitSurface(imgletras, &src, pantalla, &dest);

En definitiva el secreto para extraer letras de una imagen es encontrar una forma inteligente de calcular el
desplazamiento (src.x, src.y).

Eventos

int SDL_WaitEvent(SDL_Event * evento)


Permite capturar un evento. Retorna 0 en caso de error 1 en caso de xito.

Tambin trabajamos con la unin SDL_Event que nos permite almacenar informacin sobre el evento que
se produjo. Si bien posee varios miembros slo explicaremos algunos:
Uint8 type: Define el tipo de evento. Podemos comparar este campo con por ejemplo
SDL_MOUSEMOTION, SDL_MOUSEBUTTONDOWN, SDL_KEYDOWN , SDL_QUIT entre otros.
SDL_KeyboardEvent key: Estrucutura que sirve para identificar eventos con teclas.
Sus miembros son:
Uint8 type: SDL_KEYDOWN o SDL_KEYUP
Uint8 state: SDL_PRESSED o SDL_RELEASED
SDL_keysym keysym: Estructura que representa el mapa de teclado
SDLKey sym: Cdigo de tecla.
SDLMod mod: modificadores se utilizan para determinar si se estn
presionando teclas como shift o alt.
Para realizar esta accin debemos utilizar el producto lgico & con por ejemplo
KMOD_LSHIFT. (Notese que la L se refiere a left shift)
SDL_MouseMotionEvent motion: Define el movimiento del puntero
Uint16 x, y: posicin x,y actual
Sint16 xrel, yrel: posicin relativa a la posicin anterior.
SDL_MouseButtonEvent button: Permite identificar botones del mouse presionados
Uint8 button: Qu botn se presiono.
Uint8 state: SDL_PRESSED o SDL_RELEASED

Uint16 x, y: Coordenadas en donde fuer presionado.

Ahora veamos un ejemplo de capturas de eventos.


Ejemplo_6
Como resultado obtendremos una salida como
El puntero se ha movido. La posicin nuevqa es (246,210)
Posicin relativa: (3,1)
SDL keysym: 102 - f

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

using namespace std;

int main(int argc, char *argv[])


{
SDL_Surface *pantalla;
SDL_Event evento;
SDL_keysym keysym;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"No se ha podido inicializar SDL";
return 1;
}
atexit(SDL_Quit);
pantalla = SDL_SetVideoMode(256, 256, 16, 0);
if (pantalla == NULL)
{
cout<<"No se puede inicializar el modo de video solicitado";
return 1;
}

//Utilizamos un loop para esperar que sucedan eventos


//Notese que realizamos espera activa
while (SDL_WaitEvent(&evento) != 0)
{
/*Dentro de este while colocaremos el reconocimiento de todos
los eventos que deseemos manejar.
Utilizaremos un switch para determinar como debe responder nuestro
programa a cada uno de ellos*/

switch (evento.type) //miembro tipo de la union evento


{
case SDL_MOUSEMOTION:
//Captura del movimiento del mouse
cout<<"El puntero se ha movido ";

//Posicin actual del puntero


cout<<"La posicin nueva es: (";
cout<<evento.motion.x<<","<<evento.motion.y<<")."<<endl;

//Posicin relativa
cout<<"Posicin relativa: (";
cout<<evento.motion.xrel<<","<<evento.motion.yrel<<")"<<endl;
break;

case SDL_MOUSEBUTTONDOWN:

//Captura cuando un boton del mouse ha sido presionado


cout<<"Se ha presionado el boton ";
cout<<evento.button.button<<" en la posicin (";
cout<<evento.button.x<<","<<evento.button.y<<")"<<endl;
break;

case SDL_KEYDOWN:
//Captura cuando se ha presionado una tecla
keysym = evento.key.keysym;
cout<<"SDL keysym: "<< keysym.sym<<" - ";
cout<<SDL_GetKeyName(keysym.sym)<<endl;

/*Podemos identificar si tenemos teclas como el shift presionadas


y entonces escribir en mayscula*/
if (evento.key.keysym.mod & KMOD_LSHIFT)
cout<<"El shift izquierdo est siendo presionado"<<endl;
else
cout<<"El shift izquierdo no est presionado"<<endl;

//Podra identificar alguna letra en especial, por ejemplo la S para


salir
if (keysym.sym == SDLK_s)
exit(0);
break;

case SDL_QUIT:
//Captura cuando presionamos el boton para cerrar una ventana
exit(0);
}
}
}

Threads

Cuando estamos realizando un juego necesitamos realizar varias acciones a la vez. Imagine que estamos
programando un pinball, la bolita debe rebotar mientras el jugador puede o no estar presionando las teclas
para mover los flippers. Necesitaremos entonces un thread para mover la bolita permanentemente, es decir
dibujarla a medida que rebote, y otro para capturar las acciones del jugador.
Para lograr portabilidad entre sistemas SDL provee funciones propias para el manejo de threads que se
ejecutan de una u otra forma dependiendo del sistema en que se utilicen.

Para utilizar thread se necesita incluir SDL_thread.h

SDL_Thread *SDL_CreateThread(int (*funcion)(void *), void *data)


funcin: Puntero a una funcin que ser la que ejecutar el thread. Debe estar definida
int FuncionThreadt(void *data)
data: cualquier informacin que desee pasarse a la funcin que ejecutar el thread.

void SDL_WaitThread(SDL_Thread *thread, int *status):


Espera por la finalizacin de un determinado thread.
void SDL_KillThread(SDL_Thread *thread):
Permite abortar un thread.
Muchas veces los thread trabajan sobre recursos compartidos entre todos o bien necesitan
bloquearse a la espera de un determinado evento que debe producirse en otro thread. Para este caso se
utilizan mutex.
SDL_mutex *SDL_CreateMutex(void);
Crear un mutex.
void SDL_DestroyMutex(SDL_mutex *mutex):
Destruye un mutex creado previamente.
int SDL_mutexP(SDL_mutex *mutex) y int SDL_mutexV(SDL_mutex *mutex):
Acciones P y V de cualquier semforo.

Ejemplo de uso de thread


Ejemplo_7
Una posible salida podra ser

#include <iostream>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>

using namespace std;

int contador = 0;
SDL_mutex *mtx;
bool fin=false;

int funcionThread(void *data)


{
char *t;
t = (char *) data;

while (fin == false)


{
//Como el contrador es recurso compartido lo bloqueo para su uso
SDL_mutexP(mtx);
contador++;
cout<<"Soy el Thread "<<t;
cout<<" Aumente el contador a "<<contador<<endl;
//Libero el recurso compartido
SDL_mutexV(mtx);

SDL_Delay(rand() % 3000);
}
return 0;
}

int main(int argc, char *argv[])


{
SDL_Thread *thread1, *thread2;
mtx = SDL_CreateMutex();
thread1 = SDL_CreateThread(funcionThread, (void *)"A");
thread2 = SDL_CreateThread(funcionThread,(void *) "B");

while(contador<25)
SDL_Delay(100);

fin= true;
cout<<"Soy el main - Contador: "<<contador<<endl;
SDL_Delay(3500);
SDL_DestroyMutex(mtx);
return 0;
}

You might also like