2 Funciones gráficas

Puntos

Para pintar un pixel disponemos de dos funciones, SDL_RenderDrawPoint y SDL_RenderDrawPointF. En ambas hay que especificar el contexto de renderizado, y las coordenadas del pixel. En la primera las coordenadas son valores enteros, y en la segunda son valores en coma flotante, float.

También disponemos de dos funciones para dibujar varios puntos almacenados en un array. SDL_RenderDrawPoints, que dibuja los puntos almacenados en un array de estructuras SDL_Point con las coordenadas de cada punto en valores enteros, y SDL_RenderDrawPointsF, que dibuja los puntos almacenados en un array de estructuras SDL_FPoint, con las coordenadas de cada punto en valores float.

No podemos basar todos nuestros gráficos en pintar puntos, no sería muy práctico. Veamos ahora más funciones para mostrar gráficos.

Líneas

Para trazar líneas, o más propiamente segmentos rectos, disponemos de las funciones SDL_RenderDrawLine y SDL_RenderDrawLineF, a las que tendremos que pasar como parámetros el contexto de renderizado y las coordenadas de inicio y final de la línea. La primera usa valores enteros para las coordenadas y la segunda valores float.

También disponemos de funciones para dibujar grupos de líneas, SDL_RenderDrawLines y SDL_RenderDrawLinesF. En este caso, además de contexto de renderizado, pasaremos como parámetro un array de puntos, y un entero con el número de puntos en el array. Estas funciones trazarán las líneas entre puntos consecutivos del array. No se trazará la línea que uno el último punto con el primero. La primera función usa un array de SDL_Point y la segunda un array de SDL_FPoint.

    SDL_Point puntos[] = {{50,50}, {150,50}, {150,150}, {50,150}};
    SDL_RenderDrawLines(renderer, puntos, sizeof(puntos)/sizeof(SDL_Point));

Rectángulos

Disponemos de varias funciones para trazar rectángulos.

Podemos dividir estas funciones en dos grupos, para trazar rectángulos huecos, solo las líneas que los delimitan y para trazar figuras rectangulares rellenas.

La función SDL_RenderDrawRect traza un único rectángulo hueco. Tendremos que pasar como parámetros el contexto de renderizado y un puntero a un objeto de tipo SDL_Rect.

Para trazar varios rectángulos huecos podemos usar la función SDL_RenderDrawRects, indicando el contexto de renderizado, un array de objetos SDL_Rect, y el número de rectángulos a dibujar.

También disponemos de las versiones para rectángulos expresados en coordenadas en coma flotante, SDL_RenderDrawRectF y SDL_RenderDrawRectsF. En este caso las estructuras que contienen los rectángulos son del tipo SDL_FRect.

Para rectángulos rellenos disponemos de otras cuatro funciones similares, respectivamente: SDL_RenderFillRect, SDL_RenderFillRects, SDL_RenderFillRectF y SDL_RenderFillRectsF.

Texturas

En lo que respecta a cargar imágenes desde ficheros, la librería SDL2 tiene una limitación: solo permite cargar imágenes en formato BMP.

Esto nos limita bastante en cuanto a recursos gráficos para nuestras aplicaciones, aunque siempre podemos convertir los ficheros desde otros formatos, perderemos algunas características, como las transparencias o la compresión de datos.

También podríamos usar librerías de carga de ficheros gráficos, como FreeImage, del que tenemos un curso en la página.

Pero esto no será necesario, ya que SDL dispone de otras librerías adicionales, entre las que está SDL_Image, que nos permite cargar ficheros de imágenes desde gran variedad de formatos diferentes.

Para trabajar con imágenes disponemos de dos estructuras: SDL_Surface y SDL_Texture. La primera está destinada a ser manipulada por la CPU, es decir, se almacena en la memoria del sistema. La segunda está destinada a ser manipulada por la GPU, es decir, se almacena en la memoria de la tarjeta gráfica.

Usaremos una superficie cuando necesitemos modificar la imagen de alguna manera. En caso contrario es preferible usar una textura, ya que cualquier operación que realicemos con ella podrá beneficiarse de la aceleración hardware.

Para cargar un fichero de imagen a una superficie usaremos la función IMG_Load, indicando la ruta del fichero a cargar. Las superficies se manipulan mediante una estructura SDL_Surface.

Por supuesto, una vez hemos terminado de usar la superficie deberemos liberar los recursos que usa mediante una llamada a SDL_FreeSurface.

SDL_Surface *surface;
...
    surface = IMG_Load("loroscolor.png");
...
    SDL_FreeSurface(surface);

Para cargar un fichero de imagen a una textura usaremos la función IMG_LoadTexture, indicando el contexto de renderizado y la ruta del fichero a cargar. Las texturas se manipulan mediante una estructura SDL_Texture.

Por supuesto, una vez hemos terminado de usar la textura deberemos liberar los recursos que usa mediante una llamada a SDL_DestroyTexture.

SDL_Texture *texture;
...
    texture = IMG_LoadTexture(renderer, "loroscolor.png");
...
    SDL_DestroyTexture(texture);

Es posible convertir una superficie en una textura, usando la función SDL_CreateTextureFromSurface, indicando el contexto de renderizado y la superficie a convertir.

SDL_Surface *surface;
SDL_Texture *texture;
...
    surface = IMG_Load("loroscolor.png");
    texture = SDL_CreateTextureFromSurface(renderer, surface);
    SDL_FreeSurface(surface);
...
    SDL_DestroyTexture(texture);

Una vez creada la textura, si no necesitamos la superficie podemos liberarla, ya que la textura copia toda la información que necesita de ella, sin modificarla.

Esto es útil si necesitamos modificar la superficie de algún modo, ya que no será posible hacerlo con la textura.

Una vez tenemos una textura podemos mostrarla en la ventana de varias formas.

Mediante la función SDL_RenderCopy podemos copiar una parte o toda la textura en un área de la ventana. Para ello pasaremos como parámetros el contexto de renderizado, la textura y dos punteros a estructuras SDL_Rect. La primera contiene pa porción de la textura a copiar, o NULL para copiar toda, y la segunda la región de la ventana en la que se copiará, o NULL para ocupar toda el área de la ventana.

La estructura se deformará, cambiando la escala horizontal y vertical, si es necesario, para adaptarse al área de destino.

SDL_Rect origen = {18,47,75,59};
SDL_Rect destino = {50,50,150,150};
SDL_Point centro = {100,100};
...
    SDL_RenderCopy(renderer, texture, &origen, &destino);

Con la función SDL_RenderCopyEx tenemos mayor control. Además de los parámetros usados en la función anterior, esta función admite tres más: el ángulo de rotación, en grados y en sentido horario, el punto de giro, en una estructura SDL_Point y un último parámetros que indica si se quiere hacer un volteo en cualquiera de los dos ejes, vertical y/o horizontal.

SDL_Rect origen = {18,47,75,59};
SDL_Rect destino = {50,50,150,150};
...
    SDL_RenderCopyEx(renderer, texture, &origen, &destino, 45.0, &centro, SDL_FLIP_HORIZONTAL);

También existen versiones de estas dos funciones en las que las coordenadas del rectángulo de destino se expresa en valores en coma flotante. SDL_RenderCopyF y SDL_RenderCopyExF.

Nota: la expresión blit rendering se puede traducir, en el entorno SDL2 por transferencia de la imagen en bloque, es decir copias y mezclas de imágenes o fragmentos de imágenes. Es muy probable que empleemos la palabreja blit sin traducir, ya que no hay una expresión en nuestra lengua con tan pocas letras para expresar el mismo concepto.

Opciones de mezcla

Cuando se copia una textura en un contexto de renderizado existen diferentes opciones a la hora de combinar los pixels de la textura con los que había previamente en el contexto. Para seleccionar las opciones que más nos interesen en cada caso disponemos de tres funciones adicionales.

La función SDL_SetTextureBlendMode nos permite seleccionar los diferentes modos de mezcla. Le pasaremos como parámetros la textura y el modo de mezcla a seleccionar. Disponemos de cinco modos de mezcla predefinidos:

    Copiado sin mezcla
    Copiado sin mezcla
  • El modo sin mezcla, SDL_BLENDMODE_NONE, que simplemente copia la textura sobre el contexto, ignorando cualquier valor previo en él.
    dstRGBA = srcRGBA
    
  • Mezcla alfa
    Mezcla alfa
  • El modo de mezcla alfa, SDL_BLENDMODE_BLEND, que mezcla el valor de cada pixel de la textura con el pixel correspondiente en la ventana proporcionalmente al valor de transparencia.
    dstRGB = (srcRGB * srcA) + (dstRGB * (1-srcA))
    dstA = srcA + (dstA * (1-srcA))
    
  • Mezcla aditiva
    Mezcla aditiva
  • El modo de mezcla aditiva, SDL_BLENDMODE_ADD, que añade el valor de la textura en proporción al valor de alfa.
    dstRGB = (srcRGB * srcA) + dstRGB
    dstA = dstA
    
  • Modulación de color
    Modulación de color
  • El modo de modulación de color, SDL_BLENDMODE_MOD, que ignora el canal alfa, y calcula cada componente de color como producto del valor de los pixels de pantalla y textura.
    dstRGB = srcRGB * dstRGB
    dstA = dstA
    
  • Mezcla multiplicación
    Mezcla multiplicación
  • El modo de multiplicación, SDL_BLENDMODE_MUL, que multiplica el valor de los pixels y añade el valor de la textura en proporción inversa al canal alfa.
    dstRGB = (srcRGB * dstRGB) + (dstRGB * (1-srcA))
    dstA = dstA
    

Además de estos modos predefinidos, se pueden añadir modos de mezcla definidos por la aplicación usando la función SDL_ComposeCustomBlendMode.

Además, podemos añadir un modificador al canal alfa de la textura mediante la función SDL_SetTextureAlphaMod. Esta función toma como parámetros la textura y un valor alfa adicional que se multiplicará en las operaciones de copia de render. Para cada pixel de la textura, el nuevo valor de alfa será srcA = srcA * (alpha / 255).

Un valor 0 hará que la textura sea invisible, y un valor 255 usará los valores alfa originales de la textura.

    SDL_SetTextureAlphaMod(texture, 10);

La función SDL_SetTextureColorMod hace algo análogo con las componentes de color de una textura. En esta función pasaremos como parámetros ta textura y un multiplicador para cada una de las componentes rojo, verde y azul.

    // Eliminar la componente roja de la textura:
    SDL_SetTextureColorMod(texture, 0, 255, 255);

Ejemplo 1

Nombre Fichero Fecha Tamaño Contador Descarga
Ejemplo 1 sdl_001.zip 2024-01-07 227144 bytes 60

Nota: los programas de ejemplo asumen que las librerías SDL2 están instaladas en la misma ruta que MinGW. Si las has instalado en otra ubicación será necesario modificar las opciones de construcción (Project build options), concretamente las opciones de "Search directories" para el compilador y el enlazador.