11 Texturas y superficies
Hasta ahora, para crear y mostrar gráficos hemos usado texturas.
En versiones anteriores de SDL no existían las texturas, y los gráficos se creaban y mostraban mediante superficies. SDL 2 introduce las texturas pero mantiene las superficies.
La principal diferencia entre texturas y superficies es que las primeras pueden hacer uso de aceleración gráfica, es decir, pueden usar la memoria de las tarjetas gráficas y la capacidad de de las GPU, mientras que las superficies usan la RAM del sistema y la capacidad de la CPU.
En sistemas sin GPU las texturas se manejan con un motor basado en CPU, por lo que todo sigue funcionando, de un modo u otro.
Hay otras diferencias. Por ejemplo, las texturas no disponen de funciones para acceder directamente a los pixels, al contrario que las superficies.
Para visualizar o renderizar texturas se usa un renderer, al contrario que para las superficies que se pueden asignar directamente a una ventana.
Siempre es preferible usar texturas, y las superficies deberían usarse solo cuando sea necesario tener acceso a los pixels.
Superficies
Las superficies o surfaces son estructuras que contienen una colección de pixels que se usan en operaciones de mezcla de bits (blitting).
Nota: no hay una traducción clara del término blitting. Según Wikipedia, Bit blit (también escrito BITBLT, BIT BLT, BitBLT, Bit BLT, Bit Blt, etc., que significa transferencia de bloques de bits) es una operación de datos utilizada habitualmente en gráficos por ordenador en la que varios mapas de bits se combinan en uno solo mediante una función booleana.
Al contrario que las texturas que están optimizadas para aceleración gráfica, las superficies están destinadas a ser tratadas por la CPU.
Con las superficies nuestros programas tienen acceso a los pixels individuales de una imagen, de modo que podemos modificarlos para aplicar filtros o funciones sobre ellos.
Para manejar una superficie usaremos una estructura SDL_Surface.
Si examinamos los miembros de esta estructura podemos ver algunas de sus particularidades. Por ejemplo, el miembro format nos indica el formato de cada pixel, es decir, si se trata de índices en una paleta de colores, o si por el contrario los componente de color están codificados en bits, y cuántos bits y en qué posiciones están para cada componente, mediante máscaras de color.
Se trata de una estructura SDL_PixelFormat. Y se pueden consultar los formatos de pixel que SDL puede manejar en el enumerado SDL_PixelFormatEnum.
Los miembros w y h contienen las dimensiones anchura y altura, respectivamente, de la superficie en pixels.
Ya que los colores pueden estar codificados mediante bits, la longitud de una línea de pixels no siempre se puede calcular de forma trivial. El miembro pitch contiene esa longitud en bytes.
El miembro pixels contiene la información de color de los pixels de la superficie.
Algunas superficies usan un algoritmo de compresión RLE para almacenar los pixels. Por este motivo, si se quiere acceder a los pixels de una de estas superficies, primero habrá que bloquearla. La estructura usa los miembros locked y lock_data para ello.
Se puede definir un rectángulo de recorte en operaciones blit, lo veremos más abajo, la estructura almacena ese rectángulo en el miembro clip_rect.
Crear y liberar superficies
Hay varias formas de obtener una superficie. Podemos cargarla desde un fichero, usando la función SDL_LoadBMP. Esta función solo puede cargar ficheros en formato BMP. Afortunadamente también podemos usar IMG_Load para cargar otros tipos de ficheros de imágenes.
También podemos crear superficies de las dimensiones y con el formato de pixel deseado usando una de las funciones SDL_CreateRGBSurface o SDL_CreateRGBSurfaceWithFormat.
O mediante SDL_CreateRGBSurfaceFrom o SDL_CreateRGBSurfaceWithFormatFrom, añadiendo la información correspondiente a los pixels.
También podemos duplicar una superficie usando SDL_DuplicateSurface.
Cualquiera de las superficies obtenidas mediante estas funciones debe ser liberada cuando ya no sea necesaria, usando la función SDL_FreeSurface().
SDL_Surface *surface; SDL_Surface *surface2; ... surface = IMG_Load("loroscolor.png"); surface2 = SDL_DuplicateSurface(surface); ... SDL_FreeSurface(surface); SDL_FreeSurface(surface2);
Otra forma de obtener una superficie es a partir de una textura. Esta es la única forma de poder acceder a los pixels de una textura. Para ello se usa la función SDL_LockTextureToSurface:
SDL_LockTextureToSurface(textura, NULL, &superficie); // Procesar pixels de superficie SDL_UnlockTexture(textura);
En este caso es imprescindible que la textura haya sido creada con SDL_TEXTUREACCESS_STREAMING.
Por último podemos crear una superficie y asociarla a una ventana mediante SDL_GetWindowSurface.
Estas superficies no deben ser liberadas mediante SDL_FreeSurface(). Se liberarán automáticamente al destruir la ventana o mediante la función SDL_DestroyWindowSurface.
Podemos averiguar si una ventana tiene una superficie asociada mediante la función SDL_HasWindowSurface.
Los cambios que hagamos en la superficie asociada con una ventana no serán mostrados de forma automática. En realidad nuestra superficie asociada se comporta como un doble buffer: hacemos las modificaciones en la superficie asociada, y cuando hayamos terminado actualizamos la ventana.
Para actualizar la ventana disponemos de dos funciones:
- SDL_UpdateWindowSurface que copia toda la superficie en la ventana.
- SDL_UpdateWindowSurfaceRects que copia las áreas definidas por un conjunto de rectángulos a la ventana.
#include <sdl2/SDL.h> #include <iostream> int main( int argc, char * argv[] ) { SDL_Window *ventana; SDL_Surface *superficie; SDL_Init(SDL_INIT_EVERYTHING); ventana = SDL_CreateWindow(NULL, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS); if(SDL_HasWindowSurface(ventana)) std::cout <<"Tiene superficie" << std::endl; else { std::cout << "No tiene superficie, creamos una" << std::endl; superficie = SDL_GetWindowSurface(ventana); if(SDL_HasWindowSurface(ventana)) { std::cout << "Ahora tiene superficie" << std::endl; std::cout << "Ahora destruimos la superficie" << std::endl; SDL_DestroyWindowSurface(ventana); } else { std::cout << "No se pudo crear una superficie" << std::endl; } if(SDL_HasWindowSurface(ventana)) std::cout <<"Tiene superficie" << std::endl; else std::cout << "Ya no tiene superficie" << std::endl; } SDL_Delay(5000); SDL_DestroyWindow(ventana); SDL_Quit(); return 0; }
Bloqueo de superficies
Las superficies se pueden optimizar para que se puedan realizar operaciones con ellas más rápidamente. La optimización consiste en comprimir la información de los pixels usando el algoritmo RLE. Sin embargo, si una superficie usa RLE no podrá ser modificada directamente, antes de hacerlo es necesario bloquearla, hacer las modificaciones y después desbloquearla.
Podemos averiguar si una superficie debe ser bloqueada usando la macro SDL_MUSTLOCK.
También podemos habilitar o deshabilitar la aceleración RLE en una superficie usando la función SDL_SetSurfaceRLE, y verificar si está activa mediante la función SDL_HasSurfaceRLE
Para bloquear una superficie se usa la función SDL_LockSurface y para desbloquearla SDL_UnlockSurface.
#include <iostream> #include <sdl2/SDL.h> #include <sdl2/SDL_image.h> int main(int argc, char* argv[]) { SDL_Window *ventana; SDL_Surface *surfaceWindow; SDL_Rect re = {30,30,300,300}; Uint32 color; SDL_Init(SDL_INIT_EVERYTHING); ventana = SDL_CreateWindow("SDL_CreateRGBSurface", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 640, 480, SDL_WINDOW_SHOWN); surfaceWindow = SDL_GetWindowSurface(ventana); if(SDL_MUSTLOCK(surfaceWindow)) SDL_LockSurface(surfaceWindow); color = SDL_MapRGBA(surfaceWindow->format, 128, 0, 64, 255); SDL_FillRect(surfaceWindow, &re, color); color = SDL_MapRGBA(surfaceWindow->format, 64, 128, 0, 255); re.x += 200; re.y += 200; SDL_FillRect(surfaceWindow, &re, color); if(SDL_MUSTLOCK(surfaceWindow)) SDL_UnlockSurface(surfaceWindow); SDL_UpdateWindowSurface(ventana); SDL_Delay(1000); SDL_DestroyWindow(ventana); SDL_Quit(); return 0; }
Rectángulos
En el ejemplo anterior hemos dibujado un rectángulo en una superficie usando la función
SDL_FillRect. Esta función necesita tres parámetros. El primero es un puntero a una superficie, el segundo es un puntero a una estructura SDL_Rect que define el rectángulo a dibujar y el tercero es el color que se usará para pintar el rectángulo relleno.
A su vez, el color es un valor Uint32. Debido a que en principio no tenemos por qué conocer el formato de los pixels de la superficie, no podremos indicar el valor del color directamente, de hecho, la superficie podría tener una paleta, y el valor de color en ese caso sería un índice.
Para obtener el valor del color usaremos la función SDL_MapRGBA, indicando en el primer parámetro el formato de pixel usado por la superficie. El resto de parámetros son los valores de las componentes roja, verde, azul y alfa, respectivamente.
El formato de pixel lo podemos extraer de la propia estructura SDL_Surface, del miembro format.
SDL_Surface *surface; SDL_Rect re = {30,30,300,300}; Uint32 color; ... color = SDL_MapRGBA(surface->format, 128, 0, 64, 255); SDL_FillRect(surfaceWindow, &re, color); ... SDL_FreeSurface(surface);
La función SDL_FillRects es similar, pero permite dibujar varios rectángulos. El primer parámetro también es un puntero a una superficie, el segundo contiene un puntero a un array de estructuras SDL_Rect, el tercero el número de estructuas en el array y el cuarto el color.
Operaciones blit
Las operaciones de blit consisten en combinar dos superficies. SDL dispone de varias formas de combinarlas y de varios parámetros que se pueden ajustar para modificar el resultado.
Disponemos de tres funciones de blit para superficies:
La función SDL_BlitSurface simplemente copia una parte rectangular de una superficie de origen en un área rectangular de otra de destino. En realidad, en el rectángulo de destino se ignoran la anchura y altura, y únicamente se tienen en cuenta las coordenadas. La anchura y altura serán las mismas que en rectángulo de origen.
SDL_BlitScaled funciona igual que la anterior, pero en este caso en el rectángulo de origen si se tienen en cuenta la anchura y altura, y la zona de la superficie de origen incluida en el rectángulo de origen se escalará para ocupar todo el rectángulo de destino. El escalado es a nivel de pixel. Para escalados a escalas mayores cada pixel se convierte en un rectángulo del mismo color, para escalas menores se hace la media de los pixel.
SDL_SoftStretchLinear funciona como la anterior, per el escalado es bilineal, esto produce una imagen más suave.
Esta función es muy exigente con el tipo de las superficies a mezclar. Las dos superficies deben tener el mismo formato, y en 32 bits por pixel.
.En la imagen de la derecha se puede apreciar la diferencia entre los dos tipos de escalado, el normal a la izquierda y el bilineal a la derecha.
Las tres funciones requieren los mismos cuatro parámetros: superficie de origen, rectángulo de origen, superficie de destino y rectángulo de destino.
Por ejemplo, para poder aplicar escalado bilineal a una imagen cargada tendremos que copiar primero la superficie en la que hemos cargado la imagen a una de 32 bits. Esta superficie se podrá escalar posteriormente a una segunda superficie del mismo tipo, y finalmente podremos copiar esa superficie a la superficie de la ventana, o usarla para crear una textura, o lo que necesitemos hacer con ella.
SDL_Window *ventana; SDL_Surface *superficieLoros; SDL_Surface *superficieA; SDL_Surface *superficieB; SDL_Surface *superficieVentana; SDL_Rect orig = {250, 50, 64, 48}; SDL_Rect re = {0, 0, 32, 24}; ... superficieVentana = SDL_GetWindowSurface(ventana); superficieLoros = IMG_Load("loroscolor.png"); superficieA = SDL_CreateRGBSurface(0, superficieLoros->w,superficieLoros->h,32, superficieLoros->format->Rmask, superficieLoros->format->Gmask, superficieLoros->format->Bmask, superficieLoros->format->Amask); superficieB = SDL_CreateRGBSurface(0, superficieLoros->w,superficieLoros->h,32, superficieLoros->format->Rmask, superficieLoros->format->Gmask, superficieLoros->format->Bmask, superficieLoros->format->Amask); SDL_BlitSurface(superficieLoros, NULL, superficieA, NULL); SDL_SoftStretchLinear(superficieA, &orig, superficieB, amp;&re); SDL_BlitSurface(superficieB, &re, superficieVentana, &re); SDL_UpdateWindowSurface(ventana); ... SDL_FreeSurface(superficieLoros); SDL_FreeSurface(superficieA); SDL_FreeSurface(superficieB); SDL_DestroyWindowSurface(ventana); SDL_DestroyWindow(ventana); ...
Modos de mezcla
Disponemos de varias opciones para modificar cómo se mezclan los pixels de la superficie de origen con los existentes en la superficie de destino.
La función SDL_SetSurfaceBlendMode establece el modo de mezcla entre los cinco disponibles:
- SDL_BLENDMODE_NONE. Sin mezcla.
- SDL_BLENDMODE_BLEND. Mezcla alfa.
- SDL_BLENDMODE_ADD. Mezcla aditiva.
- SDL_BLENDMODE_MOD. Modulación de color.
- SDL_BLENDMODE_MUL. Multiplicación de color.
Adicionalmente se pueden ajustar algunos parámetros de algunos modos, La función SDL_SetSurfaceAlphaMod establece un valor alfa adicional utilizado en las operaciones de blit usando el modo de mezcla alfa.
La función SDL_SetSurfaceColorMod establece un valor de color adicional multiplicado en las operaciones de blit.
Siempre podemos obtener los valores actuales de estos parámetros mediante las funciones SDL_GetSurfaceAlphaMod y SDL_GetSurfaceColorMod.
Del mismo modo, podemos consultar el valor actual del modo de mezcla para una superficie mediante la función SDL_GetSurfaceBlendMode.
Ejemplo 10
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 10 | sdl_010.zip | 2024-01-07 | 264647 bytes | 43 |
Transparencia
A la hora de realizar mezclas de superficies podemos indicar un color como transparente, de modo que los pixels de ese color no se rendericen.
Para indicar qué color queremos considerar como transparente se usa la función SDL_SetColorKey indicando como parámetros la superficie, un valor booleano para activar o desactivar la transparencia, y un valor de color, que podemos obtener mediante la función SDL_MapRGB.
La función SDL_HasColorKey sirve para averiguar si una superficie tiene asignado un color transparente, y la función SDL_GetColorKey para obtener el valor del color de transparencia de una superficie.
Recorte
Se puede definir un área rectangular dentro de la superficie de destino de una operación blit de modo que solo ese área se vea afectada.
Ese área se conoce como rectángulo de recorte y se puede definir mediante la función SDL_SetClipRect.
SDL_Rect recorte = { 0, 0 ,320, 240 }; SDL_SetClipRect(superficieVentana, &recorte);
Para obtener el rectángulo de recorte actualmente asociado a una superficie se usa la función SDL_GetClipRect.
Ejemplo 11
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 11 | sdl_011.zip | 2024-01-07 | 4039 bytes | 47 |
Paleta
Pa función SDL_SetSurfacePalette nos permite asignar una paleta a una superficie. Como primer parámetro pasaremos un puntero a la superficie y el en segundo un puntero a la nueva paleta, almacenada en una estructura SDL_Palette.
Conversiones
Ya hemos visto que algunas funciones solo están disponibles para ciertos formatos de superficies. hasta ahora hemos solucionado esto creando nuevas superficies y copiando el contenido desde la superficie de origen a las nuevas. Sin embargo, SDL2 dispone de funciones para convertir una superficie a diferentes formatos.
La función SDL_ConvertSurface crea una nueva superficie a partir de una superficie y de una estructura de un formato de pixel.
superficieVentana = SDL_GetWindowSurface(ventana); ... superficieA = SDL_ConvertSurface(superficieOrigen, superficieVentana->format, 0);
La función SDL_ConvertSurfaceFormat hace lo mismo, pero a partir de una superficie y de una constante SDL_PixelFormatEnum.
superficieVentana = SDL_GetWindowSurface(ventana); ... superficieA = SDL_ConvertSurfaceFormat(superficieOrigen, superficieVentana->format->format, 0);
La funcción SDL_ConvertPixels hace la conversión de formato, pero de un bloque de pixels en lugar de una superficie.
Texturas
Las texturas son estructuras optimizadas para ser utilizadas por aceleración hardware, y por lo tanto sus miembros no están disponibles para la aplicación, ya que dependen en gran medida del hardware.
Las texturas están optimizadas para aceleración gráfica, de modo que, salvo que tengamos que acceder a los pixeles individuales, siempre usaremos texturas en lugar de superficies.
Hay que tener en cuenta que las texturas siempre están asociadas a un contexto de renderizado, un renderer, de modo que antes de poder utilizar cualquier textura deberemos disponer de un contexto de renderizado.
Crear y liberar texturas
Podemos crear una textura mediante la función SDL_CreateTexture, indicando el formato de pixel y sus dimensiones, que posteriormente podremos modificar. Veremos algún ejemplo cuando más abajo, cuando se explique el bloqueo de texturas.
Otra forma de crear una textura es a partir de una superficie, mediante la función SDL_CreateTextureFromSurface, como vimos en el capítulo 2:
SDL_Surface *surface; SDL_Texture *texture; ... surface = IMG_Load("loroscolor.png"); texture = SDL_CreateTextureFromSurface(renderer, surface); SDL_FreeSurface(surface); ... SDL_DestroyTexture(texture);
O podemos cargar una imagen directamente en una textura mediante la función IMG_LoadTexture:
SDL_Texture *texture; ... texture = IMG_LoadTexture(renderer, "loroscolor.png"); ... SDL_DestroyTexture(texture);
Siempre, cuando ya no necesitemos las texturas creadas, deberemos liberar los recursos que requieren usando la función SDL_DestroyTexture.
Consultar propiedades de texturas
Podemos recuperar información sobre una textura mediante la función SDL_QueryTexture. Concretamente podemos obtener su formato de pixels, el tipo de acceso y sus dimensiones.
SDL_PixelFormatEnum formato; SDL_TextureAccess acceso; int w, h; SDL_QueryTexture(textura, &formato, &acceso, &w, &h);
Si no nos interesa alguno de los valores está permitido pasar NULL como parámetro.
Mostrar texturas
El equivalente a las operaciones blit de superficies para texturas es el renderizado. Esto involucra a otra estructura, el contexto de renderizado o renderer. De modo que las texturas siempre trabajan junto a contextos de renderizado.
Así, para mostrar una textura en una ventana primero tenemos que crear un contexto de renderizado asociado a la ventana y usar una de las funciones RenderCopy.
La más simple es SDL_RenderCopy, que copia una zona rectangular de una textura a una zona rectangular de la ventana.
SDL_RenderCopy(renderer, texture, NULL, NULL);
La función SDL_RenderCopyF hace lo mismo, pero las coordenadas de los rectángulos de origen y destino se expresan con valores float.
La pareja de funciones SDL_RenderCopyEx y SDL_RenderCopyExF admiten tres parámetros más: un ángulo de rotación que se aplica al rectángulo de destino, las coordenadas del centro para la rotación, y un valor de tipo SDL_RendererFlip que indica si se debe invertir la imagen y con respecto a qué eje. La segunda función usa las versiones con coordenadas float para los rectángulos y el centro.
SDL_Rect origen = {18,47,75,59}; SDL_Rect destino = {50,50,150,150}; SDL_Point centro = {100, 100}; ... SDL_RenderCopyEx(renderer, texture, &origen, &destino, 45.0, ¢ro, SDL_FLIP_HORIZONTAL);
Debido a que se usa una técnica de doble buffer, ninguna de las operaciones sobre un contexto de renderizado será mostrada en la ventana hasta que finalmente se invoque la función SDL_RenderPresent.
SDL_RenderPresent(renderer);
Al contrario que con las superficies, para las texturas no hay funciones especificas para el escalado. La textura se escalará automáticamente en función de los tamaños de los rectángulos de origen y destino.
Lo que sí podemos hacer es elegir entre varios métodos para el escalado mediante la función SDL_SetTextureScaleMode, indicando para una textura determinada qué modo de escalado queremos utilizar: muestreo a los pixeles más próximos, lineal o ansotrópico.
SDL_SetTextureScaleMode(textura, SDL_ScaleModeLinear);
Para obtener el modo de escalado actual para una textura se usa la función SDL_GetTextureScaleMode.
Ejemplo 12
Nombre | Fichero | Fecha | Tamaño | Contador | Descarga |
---|---|---|---|---|---|
Ejemplo 12 | sdl_012.zip | 2024-01-07 | 38594 bytes | 38 |
Modos de mezcla
Al igual que con las superficies, también disponemos de varias opciones para modificar cómo se mezclan los pixels de una textura con los existentes en en el destino.
La función SDL_SetTextureBlendMode establece el modo de mezcla entre los cinco disponibles:
- SDL_BLENDMODE_NONE. Sin mezcla.
- SDL_BLENDMODE_BLEND. Mezcla alfa.
- SDL_BLENDMODE_ADD. Mezcla aditiva.
- SDL_BLENDMODE_MOD. Modulación de color.
- SDL_BLENDMODE_MUL. Multiplicación de color.
También se pueden ajustar algunos de los parámetros de algunos modos, La función SDL_SetTextureAlphaMod establece un valor alfa adicional utilizado en las operaciones de copia de texturas usando el modo de mezcla alfa.
La función SDL_SetTextureColorMod establece un valor de color adicional multiplicado en las operaciones de copia de texturas.
Para obtener los valores actuales de estos parámetros podemos usar las funciones SDL_GetTextureAlphaMod y SDL_GetTextureColorMod.
Por último, también podemos consultar el valor actual del modo de mezcla para una superficie mediante la función SDL_GetTextureBlendMode.
Todas estas opciones de mezcla funcionan igual que con las superficies, y ya los discutimos en el capítulo 2.
Modos de mezcla personalizados
Podemos personalizar nuestros propios modos de mezcla mediante la función SDL_ComposeCustomBlendMode.
Esta función nos permite definir un factor a aplicar a los colores y a las componentes alfa de origen y de destino. Los valores de estos factores se seleccionan del tipo enumerado SDL_BlendFactor.
Además se pueden seleccionar los modos de operación para los colores y las componentes alfa de origen y destino. Los valores de estos modos se seleccionan del tipo enumerado SDL_BlendOperation.
Por ejemplo:
SDL_ComposeCustomBlendMode(SDL_BLENDFACTOR_ONE, SDL_BLENDFACTOR_DST_COLOR, SDL_BLENDOPERATION_MINIMUM, SDL_BLENDFACTOR_SRC_ALPHA, SDL_BLENDFACTOR_DST_ALPHA, SDL_BLENDOPERATION_ADD);
No todos los modos de operación son soportados en todos los contextos de renderizado.
Recorte
Para texturas, y en general para cualquier función gráfica, también se puede definir un área rectangular dentro del contexto de renderizado destino de una operación gráfica de modo que solo ese área se vea afectada.
Ese rectángulo de recorte se puede definir mediante la función SDL_RenderSetClipRect.
SDL_Rect recorte = { 0, 0 ,320, 240 }; SDL_SetRenderSetClipRect(renderer, &recorte);
Para obtener el rectángulo de recorte actualmente asociado a un contexto de renderizado se usa la función SDL_RenderGetClipRect, y para saber si el recorte está activado en un contexto de renderizado se usa la función SDL_RenderIsClipEnabled.
Bloqueo de texturas
Si una textura se ha creado con un acceso de tipo SDL_TEXTUREACCESS_STREAMING será posible bloquearla.
Bloquear una estructura permite acceder a la información de sus pixels, aunque solo para modificarlos. Para bloquear una textura se usa la función SDL_LockTexture, indicando la textura a bloquear, un rectángulo con los pixels a bloquear, un puntero que recibirá la dirección de memoria con la información de los pixels y un puntero que recibirá el paso, o los bytes destinados a cada línea de pixels.
Una vez bloqueada la textura, la función SDL_UpdateTexture nos permite actualizar la información correspondiente a los pixels que queremos modificar.
Para finalizar, desbloquearemos la textura con SDL_UnlockTexture:
SDL_Texture *textura; SDL_Rect rect = {10,10,50,50}; void *buffer; int paso; ... SDL_LockTexture(textura, &rect, &buffer, &paso); for(int i=0; i<rect.h; i++) { for(int j=0; j<paso; j++) { buffer[i*paso+j] = 0; } } SDL_UpdateTexture(textura, &rect, &buffer, paso); SDL_UnlockTexture(textura);
Renderizar a textura
Por defecto, las operaciones de renderizado se hacen en una ventana, pero también es posible renderizar a una textura.
Para ello la textura debe haber sido creada con la bandera SDL_TEXTUREACCESS_TARGET.
También tendremos que averiguar si el renderizador soporta diferentes objetivos de renderizado, mediante la función SDL_RenderTargetSupported.
Para establecer una textura como objetivo de renderizado se usa la funcón SDL_SetRenderTarget, y para volver a usar la ventana como objetivo de renderizado usaremos NULL como segundo paámetro:
SDL_SetRenderTarget(renderer, textura); // Volver a usar la ventana: SDL_SetRenderTarget(renderer, NULL);
Para obtener el objetivo de renderizado actual de un contexto de renderizado se usa la función SDL_GetRenderTarget. Un valor de retorno NULL indica que el objetivo de renderizado es la ventana.
Datos de usuario
A veces es conveniente asociar ciertos datos de la aplicación a una textura. Podemos hacerlo mediante una llamada a la función SDL_SetTextureUserData, y para recuperar los datos de usuario asociados a una textura se usa la función SDL_GetTextureUserData:
struct data { ... }; SDL_Texture *textura; data datosUsuario; SDL_SetTextureUserData(textura, static_cast<void*>(datosUsuario); ... static_cast<data>(SDL_GetTextureUserData(textura));
Viewport
Generalmente, al crear un contexto de renderizado asociado a una ventana, el área de renderizado coincide con el área útil de la ventana, lo que se conoce como área de cliente. El viewport es el área rectangular asociada con el contexto de renderizado, y no tiene por qué ser la misma que el área de cliente de la ventana. Podemos modificar el tamaño y dimensiones del viewport usando la función SDL_RenderSetViewport.
Los viewports en SDL 2 se usan para representar diferentes zonas de una ventana. Por ejemplo, una zona puede estar destinada a la vista del jugador, otra a herramientas, otra a inventario, un menú, un mapa, etc.
Un viewport, una vez definido, se convertirá en la zona de renderizado total, por lo que no será necesario indicar un rectángulo de destino en funciones como SDL_RenderCopy.
SDL_Rect rect = {100,100,440,280}; SDL_RenderClear(renderer); SDL_RenderSetViewport(renderer, &rect); SDL_RenderCopy(renderer, textura, NULL, NULL); SDL_RenderPresent(renderer);
Aunque en este ejemplo el resultado sería el mismo indicando como superficie de destino el rectángulo rect, un viewport tiene la ventaja de que las coordenadas a la hora de dibujar en el contexto de renderizado serán relativas al viewport, y no a la ventana. Por lo tanto será útil cuando los gráficos a visualizar sean más complejos que un simple mapa de bits.
Para obtener el tamaño del viewport se usa la función SDL_RenderGetViewport.
.