AnteriorPosterior

7. Pantalla completa. Doble buffer.

  Curso: Introducción a los gráficos en C con SDL

7.1. Pantalla completa


Hasta ahora, todo lo que hemos hecho ha sido para que se viera en ventana. Si queremos que trabaje en pantalla completa, los cambios son mínimos: basta añadir SDL_FULLSCREEN a la lista de parámetros que indicamos al escoger el modo de pantalla. Estos parámetros se deben indicar separados por una barra vertical (|), porque entre ellos se va a realizar una operación OR (suma lógica) a nivel de bit:

  screen = SDL_SetVideoMode( 640, 480, 16, SDL_FULLSCREEN | SDL_HWSURFACE );

7.2. El doble buffer


Si alguien ha intentado crear algo con varias imágenes a la vez en pantalla, es probable que se haya encontrado con que el resultado parpadea.

El motivo es que mandamos información a la pantalla en distintos instantes, por lo que es fácil que alguno de todos esos bloques de información llegue en un momento que no coincida con el barrido de la pantalla (el movimiento del haz de electrones que redibuja la información que vemos).

La primera solución es preparar toda la información, trozo a trozo, en una "imagen oculta", y sólo volcar a la pantalla visible cuando la imagen está totalmente preparada. Esta técnica es la que se conoce como "doble buffer".

El segundo paso es sincronizar con el barrido, algo que en la mayoría de bibliotecas hace una función llamada "retrace" o "sync", y que en SDL se hace automáticamente cuando volcamos la información con "SDL_Flip".

Ya en la práctica, en SDL, comenzaremos por añadir el parámetro correspondiente (SDL_DOUBLEBUF) cuando entramos a modo gráfico:

screen=SDL_SetVideoMode(640,480,16,SDL_HWSURFACE|SDL_DOUBLEBUF);

A la hora de dibujar, no lo hacemos directamente sobre "screen", sino sobre una superficie ("surface") auxiliar. Cuando toda esta superficie está lista, es cuando la volcamos a la pantalla, así:

SDL_BlitSurface(fondo, NULL, pantallaOculta, &destino);
SDL_BlitSurface(protagonista, NULL, pantallaOculta, &destino);
...
SDL_BlitSurface(pantallaOculta, NULL, screen, &destino);
SDL_Flip(screen);


Sólo queda un detalle: ¿cómo reservamos espacio para esa pantalla oculta?

Si la pantalla oculta es del mismo tamaño que nuestro fondo o que alguna otra imagen, nos puede bastar con cargar la imagen :

fondo = SDL_LoadBMP("fondo.bmp");

Si no es el caso (como ocurre en nuestro ejemplo), podemos usar "SDL_CreateRGBSurface", que reserva el espacio para una superficie de un cierto tamaño y con una cierta cantidad de colores, así:

  pantallaOculta = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 16,
     0,0,0,0);

(el parámetro SDL_SWSURFACE indica que no se trabaje en memoria física de la tarjeta, sino en memoria del sistema; 640x480 es el tamaño de la superficie; 16 es la cantidad de color -16bpp = 655356 colores-; los otros 0,0,0,0 se refieren a la cantidad de rojo, verde, azul y transparencia -alpha- de la imagen).


Con todo esto, podemos mejorar el ejemplo anterior para que trabaje a pantalla completa, usando doble buffer.

De paso, aprovecharemos para crear una función "keypressed", que compruebe si hay alguna tecla pulsada, como comentamos en el apartado anterior, reescribiremos "getch" para que se base en ella, y permitiremos que se puedan mantener teclas pulsadas.

El resultado podría ser este:

  1: /*******************************************
2: * Introduccion a SDL - 7 *
3: * Curso de C, Nacho Cabanes *
4: *******************************************/

5:
6: #include <stdlib.h>
7: #include <SDL/SDL.h>
8:
9:
10: /*** Funcion alternativa a keypressed() ***************/
11: int nSDL_keypressed() {
12: SDL_Event suceso;
13:
14: while (SDL_PollEvent(&suceso)) { /* Comprobamos sucesos */
15: if (suceso.type == SDL_KEYDOWN) /* Si es tecla pulsada */
16: return suceso.key.keysym.sym; /* La devolvemos */
17: }
18: /* Si no hay tecla pulsada, devolvemos 0 (falso) */
19: return 0;
20: }
21:
22:
23: /*** Funcion alternativa a getch() ***************/
24: int nSDL_getch() {
25: int tecla;
26: /* Imitacion de "getch()" para SDL */
27: while (1) { /* Repetimos indefinidamente */
28: tecla = nSDL_keypressed(); /* Miramos si se ha pulsado algo */
29: if (tecla) return tecla; /* Y devolvemos cuando lo haya */
30: }
31: }
32:
33:
34: /*** Cuerpo del programa ***************************/
35: int main(int argc, char *argv[])
36: {
37: SDL_Surface *screen;
38: SDL_Surface *pantallaOculta;
39: SDL_Surface *fondo;
40: SDL_Surface *protagonista;
41: SDL_Rect destino;
42: int i, j;
43: int xProt = 320, yProt= 400;
44: int terminado = 0;
45: int tecla;
46:
47:
48: /* Tratamos de inicializar la biblioteca SDL */
49: if (SDL_Init(SDL_INIT_VIDEO) < 0) {
50: printf("No se pudo inicializar SDL: %s\n", SDL_GetError());
51: exit(1);
52: }
53:
54: /* Preparamos las imagenes a mostrar */
55: fondo = SDL_LoadBMP("fondo.bmp");
56: protagonista = SDL_LoadBMP("protag.bmp");
57: /* Y la oculta, para que tenga tamaño correcto */
58: pantallaOculta = SDL_CreateRGBSurface(SDL_SWSURFACE, 640, 480, 16,
59: 0,0,0,0);
60:
61: /* El protagonista debe tener contorno transparente */
62: SDL_SetColorKey(protagonista, SDL_SRCCOLORKEY,
63: SDL_MapRGB(protagonista->format, 0, 0, 0));
64:
65: /* Si todo ha ido bien, hacemos algo:
66: entrar a modo grafico y cambiar el titulo de la ventana */

67: screen = SDL_SetVideoMode( 640, 480, 16,
68: SDL_FULLSCREEN | SDL_HWSURFACE | SDL_DOUBLEBUF);
69: if(screen == NULL) {
70: printf( "Error al entrar a modo grafico: %s\n", SDL_GetError() );
71: SDL_Quit();
72: return -1;
73: }
74:
75: /* Permitimos que nuestro "getch" repita cada 20 ms */
76: SDL_EnableKeyRepeat(20, 20);
77:
78: /* Titulo de la ventana */
79: SDL_WM_SetCaption( "Hola mundo 7!", "Hola Mundo 7!" );
80:
81: /* Parte repetitiva: hasta que se elija terminar */
82: while(terminado == 0) {
83:
84: /* Dibujamos la imagen de fondo */
85: /* Como tiene 207 x 211 pixeles, la repetimos varias veces */
86: for (i=0; i<3; i++)
87: for (j=0; j<3; j++) {
88: destino.x=207*i;
89: destino.y=211*j;
90: SDL_BlitSurface(fondo, NULL, pantallaOculta, &destino);
91: }
92:
93: /* Dibujamos el protagonista */
94: destino.x=xProt;
95: destino.y=yProt;
96: SDL_BlitSurface(protagonista, NULL, pantallaOculta, &destino);
97:
98: /* Actualizamos la pantalla */
99: destino.x=0;
100: destino.y=0;
101: SDL_BlitSurface(pantallaOculta, NULL, screen, &destino);
102: SDL_Flip(screen);
103:
104: /* Esperamos una tecla */
105: tecla = nSDL_getch();
106:
107: /* Y vemos cual ha sido */
108: if (tecla == SDLK_ESCAPE) { terminado = 1; }
109: if (tecla == SDLK_UP) { yProt -= 2; }
110: if (tecla == SDLK_DOWN) { yProt += 2; }
111: if (tecla == SDLK_LEFT) { xProt -= 3; }
112: if (tecla == SDLK_RIGHT) { xProt += 3; }
113:
114: /* Y esperamos 50 ms antes de repetir */
115: SDL_Delay( 50 );
116: }
117:
118: /* Finalmente, preparamos para salir */
119: SDL_Quit();
120: return 0;
121: }

Nota: este fuente todavía tiene cosas que mejorar. No estamos comprobando si hemos podido reservar espacio para la pantalla oculta, ni si existen los ficheros de datos. Poco a poco lo iremos mejorando.

Actualizado el: 06-06-2006 17:05

AnteriorPosterior