Wednesday 24 April 2019

Mostrar una imagen en la pantalla

Ahora que ya logramos abrir una ventana, ¡vamos a ponerle una imagen!.

Anteriormente, solíamos poner todo el código en la función main. Ya que se trataban de programas sencillos entonces si se valía, pero en la vida real (como al hacer videojuegos) quizá quieras tener tu código disponible en forma de modular. Esto significa que tendrás tu código dividido en pequeñas piezas, fáciles de depurar y reutilizar.

En este caso, significa que tendremos funciones para manejar la inicialización, carga de los medios y el cierre de la aplicación SDL. Vamos a declarar estas funciones cerca del tope de nuestro fichero main.cpp.

Esta función (que se localizará en el scope global en nuestro fichero) va ocasionar problemas en compiladores de C, debido a que este lenguaje como sabrán, no soporta sobrecarga de métodos. Esto no será problema si usamos un compilador de C++.

Aquí declaramos un par de variables globales, Normalmente querríamos evitar usar variables globales en programas grandes. La razón por la que las usamos aquí es porque buscamos tener el código fuente tan simple como sea posible, pero en proyectos grandes las variables globales hacen las cosas más complicadas. Ya que este programa solo es de un solo fichero no tendremos que preocuparnos mucho por esto.

Aquí nos topamos con un nuevo tipo de dato llamado SDL_Surface. Este es solamente una tipo de dato de imagen que contiene los pixeles de una imagen junto con los datos que necesita para renderizarla. Las SDL_Surface utilizan renderizado por software, por lo que funciona por medio del CPU. Es posible usar aceleración por hardware pero es un poco más difícil, por ahora vamos a ver la versión sencilla, ya veremos en nuevas entradas como hacerlo por hardware.

Ah, por cierto, recuerda siempre inicializar tus punteros. Los vamos a establecer en NULL inmediatamente de haberlos creado.

Como podrás ver, tomamos todo el código de inicialización de SDL y creación de la ventana y lo pusimos en su propia función. Un cambio que hicimos es que ahora hay una llamada a SDL_GetWindowSurface.

Queremos mostrar imágenes dentro de la ventana, por lo que tenemos que meter la imagen en la ventana. Para eso llamamos a SDL_GetWindowSurface para obtener un puntero a la superficie contenida por la ventana.

En la función loadMedia vamos a cargar nuestra imagen usando la función de SDL, SDL_LoadBMP. Esta función toma la ruta de un archivo bmp y regresa la superficie cargada. Si la función regresa NULL, esto significa que ocurrió un error, el cual vamos a mostrar en la consola usando SDL_GetError.

Un detalle muy importante a notar es que este snippet de código asume que tienes un archivo llamado sonyericsson.bmp en el directorio de trabajo. El directorio de trabajo es donde la aplicación piensa que está operando. Normalmente, es donde se encuentra el ejecutable, pero algunos programas como Visual Studio cambian el directorio a donde se encuentra el fichero vcxproj. En caso de que no se pueda cargar tu imagen, asegúrate de que se encuentre en el directorio correcto.

Una vez más, si tu programa se ejecuta correctamente pero no puede localizar la imagen, es probable que tengas un problema con relación al directorio de trabajo. Estos funcionan de manera distinta de SO a SO, de IDE a IDE etc. Si no encuentras una solución a un problema, te recomiendo mover el fichero bmp hasta que encuentres donde debe de ir.

En nuestra función de limpieza, destruimos la ventana y cerramos SDL como lo hemos hecho anteriormente, pero también nos ocupamos de la superficie que habíamos cargado. Esto lo hacemos al liberarla de la superficie. No te preocupes por la superficie de la ventana, la función SDL_DestroyWindow se encarga de destruirla también.

Asegúrate de hacerte el hábito de tener tus punteros apuntando a NULL cuando no están apuntando a nada.

En nuestra función main vamos a inicializar SDL y cargar la imagen, si tuvimos éxito, vamos a aplicar un blit desde la superficie cargada hacia la superficie de la pantalla, usando la función de SDL, SDL_BlitSurface.

Lo que el proceso de blitting hace es tomar una superficie de origen y estamparle una copia en la superficie destino. El primer argumento de esta función es la imagen (superficie) de origen, el tercer argumento es la imagen de destino. Por ahora no te preocupes por el segundo y cuarto argumento.

Si solo tuviéramos este comando de dibujo, no veríamos nada en la pantalla. Todavía nos falta un paso.

Después de dibujar todo en la pantalla que queremos mostrar por este frame tenemos que actualizar la pantalla usando la función SDL_UpdateWindowSurface. Cuando dibujas algo en la pantalla, no estás dibujando directamente en la pantalla que ves, por defecto, la mayoría de los renderizadores tienen un buffer doble, denominados el buffer frontal y el buffer trasero (o anterior y posterior).

Cuando ejecutas llamadas como SDL_BlitSurface, renderizas al buffer posterior. Lo que ves en la pantalla es el buffer anterior. La razón por la que funciona de esta manera es porque la mayoría de los frames requiere el dibujado de múltiples elementos en la pantalla. Si solo tuviéramos un solo buffer, seríamos capaces de ver como el frame se va llenando de dibujos, es decir, veríamos el frame sin terminar. Así que lo que buscamos es terminar de dibujar en el buffer posterior primero, y una vez que hayamos terminado, cambiar los buffer para que el usuario pueda ver el frame terminado.

Esto también significa que no llamas a la función SDL_UpdateWindowSurface después de cada blit, sino que una vez que todos los blits para el frame actual estén listos.



¡Descarga el código fuente y los recursos de esta publicación aquí!

No comments:

Post a Comment