Professional Documents
Culture Documents
Investigaciones Tecnolgicas
Sistemas Operativos
Jefe de Ctedra:
Autor:
Fabio E. Rivalta
Sabrina M. Alonso
Ao:
2012
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.
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 bien SDL est compuesta por varios subsistemas, en esta gua abarcaremos algunos temas.
Funciones generales
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;
SDL_DOUBLEBUF -
Doble buffer.
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;
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.
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.
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;
}
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 */
//Desbloqueamos la superficie
SDL_UnlockSurface(pantalla);
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.
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.
#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;
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.
#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;
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;
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
cout<<"No se ha podido inciailizar SDL"<< SDL_GetError()<<endl;
return 1;
}
atexit(SDL_Quit);
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);
100;
dest.y = 90;
dest.w = imagen->w;
dest.h = imagen->h;
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.
if (SDL_Init(SDL_INIT_VIDEO) != 0)
atexit(SDL_Quit);
SDL_FreeSurface(fondo);
SDL_FreeSurface(imagen);
return 0;
}
if (SDL_Init(SDL_INIT_VIDEO) != 0)
atexit(SDL_Quit);
{
cout<<"No se ha podido realizar la conversin";
return 1;
}
SDL_FreeSurface(aux);
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;
Texto en SDL
Si bien hay bibliotecas para el manejo de texto que pueden descargarse, veremos como hacerlo a partir de
una imagen.
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);
}
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;
src.x= 24* 4;
src.y= 0;
En definitiva el secreto para extraer letras de una imagen es encontrar una forma inteligente de calcular el
desplazamiento (src.x, src.y).
Eventos
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
#include <SDL/SDL.h>
#include <stdlib.h>
#include <iostream>
//Posicin relativa
cout<<"Posicin relativa: (";
cout<<evento.motion.xrel<<","<<evento.motion.yrel<<")"<<endl;
break;
case SDL_MOUSEBUTTONDOWN:
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;
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.
#include <iostream>
#include <stdlib.h>
#include <SDL/SDL.h>
#include <SDL/SDL_thread.h>
int contador = 0;
SDL_mutex *mtx;
bool fin=false;
SDL_Delay(rand() % 3000);
}
return 0;
}
while(contador<25)
SDL_Delay(100);
fin= true;
cout<<"Soy el main - Contador: "<<contador<<endl;
SDL_Delay(3500);
SDL_DestroyMutex(mtx);
return 0;
}