Anuncios Google

Curso SDL para Windows XP en C++ (Capitulo 2)

Tutoriales de Programaciones

No es mi intención explicar concienzudamente cada función de las librerias "SDL", para esos asuntos consultar la página oficial. La idea es ir creando una librería personalizada de fácil manejo en español. Esta librería se llamará "Mi_Libreria.h" como encabezado y "Mi_Libreria.cpp" donde estarán contenidas las funciones.

El Sistema Gráfico

Usaremos unas definiciones estandares incluidas en "Mi_Libreria.h":

#define Pantalla_X 640 // Pixels ancho pantalla.
#define Pantalla_Y 480 // Pixels alto pantalla.
#define Pantalla_bpp 16 // Bits por Pixels.
#define Pantalla_Ventana SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE
#define Pantalla_Completa SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN
#define Volumen_Musica 64 // Valores de 0 a 128.
#define Volumen_Sonido 64 // Valores de 0 a 128.
#define Frames_Segundo 20 // Son los fotogramas por segundo.

Y estas variables Externas:

extern SDL_Surface *pantalla; // Es la superficie principal o pantalla.
extern SDL_Event evento; // Eventos del Teclado y Ratón.
extern int terminar; // Variable para bucle principal. 

Todas las funciones explicadas a continuación son a modo de ejemplo e instrucción, las verdaderas funciones son las contenidas en "Mi_Libreria.cpp", al final de este tutorial veremos un pequeño ejemplo de su uso.

Los Tipos De Datos

Estos son los tipos de datos que usaremos normalmente, los primeros son definidos por SDL y los últimos son nativos del "C". Además hay una serie de estructuras también definidas por SDL.

  • Uint8 -------> unsigned char -> 08 bit (01 byte) -> Uint8 x = 255U;
  • Uint16 ------> unsigned int --> 16 bit (02 byte) -> Uint16 x = 123U;
  • Uint32 ------> unsigned int --> 32 bit (04 byte) -> Uint32 x = 123UL;
  • Uint64 ------> unsigned int --> 64 bit (08 byte) -> Uint64 x = 123ULL;
  • Sint8 -------> signed char ---> 08 bit (01 byte) -> Sint8 x = -127;
  • Sint16 ------> signed int -----> 16 bit (02 byte) -> Sint16 x = -123;
  • Sint32 ------> signed int -----> 32 bit (04 byte) -> Sint32 x = -123L;
  • Sint64 ------> signed int -----> 64 bit (08 byte) -> Sint64 x = -123LL;
  • float --------> float -----------> 32 bit (04 byte) -> float x = 5.456F;
  • double ------> double --------> 64 bit (08 byte) -> double x = 5.456;
  • long double -> long double --> 80 bit (10 byte) -> Long double x = 5.456L;

 

Iniciando la Pantalla

#define Pantalla_X 640 // Pixels ancho pantalla.
#define Pantalla_Y 480 // Pixels alto pantalla.
#define Pantalla_bpp 16 // Bits por Pixels.
#define Pantalla_Ventana SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_RESIZABLE
#define Pantalla_Completa SDL_SWSURFACE | SDL_DOUBLEBUF | SDL_FULLSCREEN
#define Frames_Segundo 20 // Son los fotogramas por segundo.
void Inicia_SDL( const char *nombre_ventana ) {
 
    atexit( SDL_Quit );
    if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) == -1) {
 
        printf("No se pudo iniciar la libreria SDL: %s\n", SDL_GetError());
        exit(1); 
 
    }
    pantalla = SDL_SetVideoMode( Pantalla_X, Pantalla_Y, Pantalla_bpp, Pantalla_Ventana );
    if (!pantalla) {
 
        printf("No se pudo iniciar el modo de pantalla: %s\n", SDL_GetError());
        exit(1); 
 
    }
    SDL_WM_SetCaption( nombre_ventana, NULL ); // Pone nombre a la ventana. 
 
} 

SDL_Surface *SDL_SetVideoMode( int width, int height, int bpp, Uint32 flags );

  • width, height: son el ancho y alto (medido en pixeles) del modo de video que queremos iniciar.
  • bpp: es el número de bits por pixel que queremos, puede ser 8, 16, 24 o 32 bits, siendo el más utilizado el de 16 bits.
  • flags:
    • SDL_SWSURFACE: Crea la superficie en la memoria del sistema (RAM).
    • SDL_HWSURFACE: Crea la superficie en la memoria de video (Tarjeta de video).
    • SDL_ASYNCBLIT: Habilita el uso del volcado asíncrono a la superficie de visualización. Esto provocará usualmente la ralentización del volcado en máquinas de una única CPU, pero puede aumentar la velocidad en sistemas SMP.
    • SDL_ANYFORMAT: Normalmente, si una superficie de video de la profundidad requerida (bpp) no está disponible, SDL emulará una con una superficie en sombras (shadow surface). Pasando SDL_ANYFORMAT se evita esto y causa que SDL use la superficie de vídeo, independiente de la profundidad.
    • SDL_HWPALETTE: Permite el acceso exclusivo a la paleta. Sin este flag no siempre podrás obtener colores utilizando las funciones SDL_SetColors() y SDL_SetPalette().
    • SDL_DOUBLEBUF: Habilita la utilización de búfer doble. Una llamada a SDL_Flip() intercambiará los búferes y actualizará la pantalla. Si la utilización de búfer doble no se encuentra habilitada, entonces SDL_Flip() simplemente realizará un SDL_UpdateRect() en toda la pantalla.
    • SDL_FULLSCREEN: SDL intentará usar el modo a pantalla completa. Esto no siempre es posible.
    • SDL_OPENGL: Crea un contexto de renderizado OpenGL. Se deben haber establecido previamente los atributos OpenGL con SDL_GL_SetAttribute().
    • SDL_OPENGLBLIT: Como el anterior pero permite operaciones de volcado normales.
    • SDL_RESIZABLE: Crea una ventana de tamaño modificable. Si la ventana cambia de tamaño se generará el evento.
    • SDL_VIDEORESIZE: y se puede llamar nuevamente a la función SDL_SetVideMode(), para cambiar al nuevo tamaño.
    • SDL_NOFRAME: Si es posible, SDL crea un ventana sin barra de título u otras decoraciones. El modo FullScreen activa automáticamente este flag.

Estructura del color

typedef struct {
 
    Uint8 r; // Intensidad de la componente roja. ( 0 <-> 255 )
    Uint8 g; // Intensidad de la componente verde.( 0 <-> 255 )
    Uint8 b; // Intensidad de la componente azul. ( 0 <-> 255 )
    Uint8 unused; // Utilizado para el canal Alpha. ( 0 <-> 255 ) 
 
} SDL_Color; 

El rango de valores de r, g y b comprende desde 0 hasta 255, donde 0 es la menor intensidad de color y 255 la mayor posible. Si quisiéramos, por ejemplo, mostrar el color verde puro deberíamos inicializar estos valores con (0, 255, 0) con lo que estaríamos indicando que la intensidad de color para el rojo sería 0, 255 para el verde y 0 para el azul. El miembro que no se usa es libre para almacenar la información que queramos, como puede ser el canal alpha, o bien cualquier otra cosa que consideremos oportuna.

Ejemplos:

SDL_Color verde = { 0, 255, 0 };
SDL_Color rojo;
rojo.r = 255;
rojo.g = 0;
rojo.b = 0;
 
SDL_Color azul_trans = { 0, 0, 255, 127 };
 
SDL_Color Amarillo_trans;
Amarillo_trans.r = 255;
Amarillo_trans.g = 255;
Amarillo_trans.b = 0;
Amarillo_trans.unused = 127; 

Estructura Palette

No la vamos a utilizar puesto que sólo sirve para 256 colores, pero la veremos brevemente.

typedef struct {
 
    int ncolors; // número de colores usados en la paleta.
    SDL_Color *colors; // puntero a la estructura COLOR. 
 
} SDL_Palette; 

La definición de este tipo de datos es bastante lógica ya que vamos a guardar una colección de colores. La paleta es solo usada en los modos de 8 bits por pixel, es decir los modos que funcionan a 256 colores. Una paleta de color no es mas que un subconjunto o agrupación de colores de los que podemos mostrar por pantalla que van a ser de uso común en el desarrollo de la aplicación. Por ejemplo si disponemos de 256 colores y vamos a desarrollar un videojuego o una aplicación en el que vamos a usar sólo 16 podemos crear una paleta con estos 16 colores y utilizarla para no tener que hacer referencia cada vez a la cantidad de colores en RGB. Es decir, cada vez que quiera utilizar el color azul no tendremos que especificar el (0,0,255) por componentes RGB si no acceder a, por ejemplo, la primera posición de nuestra paleta donde lo hemos almacenado previamente.

 

Estructura PIXEL:

Por desgracia un Pixel no usa el formato SDL_Color que vimos anteriormente, es más nisiquiera usa el mismo formato en un mismo ordenador. El color de un pixel depende de los "bpp" bit por píxel, de la máscara, del desplazamiento de color y de la pérdida de precisión. Vamos un auténtico lío en el cual no pienso entrar a fondo.

Cada uno de estos píxeles tendrá asociado un color para los que se destinará un determinado número de bits (bits por píxel o bpp) que determinaran la calidad, medida en profundida de color, de la imagen. Los valores más comunes para el bpp son:

  • 8 bits (256 colores)
    • Debemos usar una paleta para establecer los distintos 256 posibles colores.
  • 16 bits (65,536 colores o Highcolor)
    • Existen distintas combinaciones para el uso de estos 16 bits. Las más comunes son:
    • 5 bits para rojo, 6 bits para verde y 5 para azul. Se utiliza esta distribución porque el ojo humano distingue más colores verdes que otros.
    • 4 bits para rojo, 4 bits para verde, 4 para azul y 4 para transparencias. Este es un modelo más equitativo.
  • 24 bits (16,777,216 colores o Truecolor)
    • Este es el modelo RGB puro. Se destinan 8 bits para cada color que cubren todo el espectro.
  • 32 bits (16,777,216 colores)
    • Utiliza RGB más 8 bits destinados a canales alpha o transparencias.

typedef struct {
 
    SDL_Palette *palette;
    Uint8 BitsPerPixel;
    Uint8 BytesPerPixel;
    Uint32 Rmask, Gmask, Bmask, Amask;
    Uint8 Rshift, Gshift, Bshift, Ashift;
    Uint8 Rloss, Gloss, Bloss, Aloss;
    Uint32 colorkey;
    Uint8 alpha; 
 
} SDL_PixelFormat; 

  • palette: Es un puntero a la paleta de colores. Si este campo apunta a NULL la profundidad de color (bpp) es superior a 8 bits, ya que esos modos no usan paletas.
  • BitsPerPixel: Es el número de bits usado para representar cada píxel en la superficie. Suele tomar los valores 8, 16, 24 o 32 bits. Cuanto mayor es el número, mayor es la cantidad de colores que se pueden representar. Si el valor de este campo es 8 entonces existirá una paleta apuntada por el miembro palette.
  • BytesPerPixel: Número de bytes utilizado para representar cada píxel en la superficie. Es equivalente a BitsPerPixel pero almacenado en bytes. Los valores que puede tomar son análogos a los de BitsPerPixel pero en bytes que son 1, 2, 3 o 4.
  • xmask: Máscara binaria utilizada para obtener el valor del componente de color x que es R para rojo, G para verde, B para azul, A para alpha.
  • xshift: Es el desplazamiento binario hacia la izquierda para el componente de color x en el valor del píxel. Mismas equivalencias, naturalmente, que el parámetro anterior con respecto a x.
  • xloss: Es la pérdida de precisión de cada componente de color 2RGBAloss. Mismas equivalencias con respecto a x que el apartado anterior.
  • colorkey: Color clave es el color que se establece como transparente en la superficie. El color almacenado en este parámetro no se mostrará por pantalla. Estudiaremos este aspecto con más detenimiento.
  • alpha: Parámetro para el alpha blending. Entre 0 - 255. Básicamente es la cantidad de transparencia aplicada. Este concepto será presentado en secciones posteriores a la actual con más detalle.

El manejo del color en hexadecimal puede ser engorroso. Es conveniente utilizar funciones que nos transformen del tipo de color que vamos a usar "SDL_Color" a "Color Hexadecimal Puro y Duro Uint32".

Estas funciones son:

  • Uint32 SDL_MapRGB( SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b );
  • Uint32 SDL_MapRGBA( SDL_PixelFormat *fmt, Uint8 r, Uint8 g, Uint8 b, Uint8 a );
  • void SDL_GetRGB( Uint32 pixel, SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b );
  • void SDL_GetRGBA( Uint32 pixel, SDL_PixelFormat *fmt, Uint8 *r, Uint8 *g, Uint8 *b, Uint8 *a );

SDL_MapRGB:

Según el formato del píxel (bpp) la representación de un color puede variar. Estas funciones se encarga que a partir del formato de píxel y la cantidad de cada color RGB que queramos establecer devuelva el color en dicho formato de píxel. La función necesita un puntero al formato del pixel, por lo que utilizaremos normalmente el campo format de SDL_Surface además de la intensidad de color rojo, verde y azul. La función devuelve un número que contiene el color demandado en un formato manejable por SDL. Si el bpp es de 16 bits el valor retornado será del tipo Uint16 y si es de 8 bits el tipo devuelto será de tipo Uint8. Existe una variante de esta función con un parámetro más añadido que nos permite trabajar con canales alpha.

SDL_GetRGB:

Estas funciones reciben como parámetro de entrada el color y el formato del píxel y devuelve por referencia en r, g y b las componentes del píxel en cuestión. En la segunda función además devuelve la componente del canal alpha.

Ejemplo de Funciones:
// Crea un Color_RGB usando un Color_SDL

Uint32 Crea_Color_RGB( SDL_Surface *imagen, SDL_Color color ) {
 
    return SDL_MapRGBA( imagen->format, color.r, color.g, color.b, color.unused ); 
 
} 

// Crea un Color_SDL usando un Color_RGB

SDL_Color Crea_SDL_Color( SDL_Surface *imagen, Uint32 color_rgb ) {
 
    SDL_Color color = { 0, 0, 0, 0 };
    SDL_GetRGBA( color_rgb, imagen->format, &color.r, &color.g, &color.b, &color.unused );
    return color; 
 
} 

Estructura RECTANGULO:

El sistema de coordenadas que utiliza SDL es diferente al sistema cartesiano de coordenadas que estamos acostumbrados a utilizar. La posición (0,0) se establece en la esquina superior izquierda de la superficie o pantalla principal. Cualquier valor positivo sobre x o y supone un desplazamiento hacia la derecha, en el caso de x, o hacia abajo, en el caso de y en la superficie en cuestión. Un valor negativo representa una posición fuera de la pantalla.

 

 

typedef struct {
 
    Sint16 x, y; // x,y posicion en pantalla
    Uint16 w, h; // w - Ancho h - Alto 
 
} SDL_Rect; 

  • Las variables "x" e "y" hacen referencia a la posición en píxeles sobre la pantalla o superficie principal donde queremos posicionar nuestra superficie. Son variables enteras con signo ya que la posición, en un momento dado, podría ser negativa y estar fuera de la pantalla visible. El rango de estas variables llega desde -32,768 hasta 32,767 lo que es más que suficiente para el tipo de dato que representa esta variable. La posición (x, y) tiene como referencia la esquina superior izquierda de la superficie principal que es la que comandará la posición del mismo en la ventana o pantalla.
  • Las variables "w" y "h" almacenan el ancho y alto en píxeles de la superficie a la que envuelve la superficie rectangular. Estas variables son enteras sin signo, de 16 bits, ya que el tamaño de una superficie no puede ser negativo. El rango de estas variables se comprende entre 0 y 65,535 más que suficiente, de nuevo, para caracterizar el ancho y alto de una superficie rectangular.

Un rectángulo abarca la superficie comprendida desde la posición (x,y) hasta la (x + w, y + h). Las otras dos esquinas vienen dadas por (x, y + h) la esquina que se encuentra abajo a la izquierda y (x + w,y) la que se corresponde con la esquina superior derecha.

Estructura de una SURFACE - SUPERFICIE

typedef struct SDL_Surface {
 
    Uint32 flags; // solo lectura
    SDL_PixelFormat *format; // solo lectura
    int w, h; // solo lectura
    Uint16 pitch; // solo lectura
    void *pixels; // lectura y escritura clipping information
    SDL_Rect clip_rect; // solo lectura
    int refcount; // Read-mostly 
 
} SDL_Surface;

 

  • flags: Son unas banderas que indican las características de la superficie en cuestión. Estas banderas están definidas mediante constantes y definen aspectos como donde crear la superficie o si habilitamos el doble búffer.
  • format: Puntero al formato de píxel de la superficie, es decir, en que formato estará la superficie en cuestión.
  • w: Ancho de la superficie en píxeles. Es una variable sin signo ya que, junto a h no pueden tomar valores negativos. Una superficie en SDL es rectángular. No tiene sentido que tenga un valor negativo para indicar su anchura.
  • h: Altura de la superficie en píxeles. Es del mismo tipo que w.
  • pitch: Corresponde a la longitud de la scanline (linea de escaneo) de la superficie en bytes. Representa el ancho real de la superficie en memoria en bytes, mientras que w representa el ancho visible de la superficie. El valor del pitch siempre es mayor que el valor del ancho visible. Esto provoca tener un excedente de bits para representar una superficie. La razón de tener esta zona perdida de memoria es para tener los datos alineados. Esto provoca que cada línea de nuestra superficie esté alineada en memoria con lo que conseguimos un acceso más rápido. Puedes observar lo que ocurre en la pitch. Para poder acceder directamente a un pixel (x,y) accederemos por el cálculo de posición (y * pitch) + (x * bytesPorPixel).
  • pixels: Puntero al comienzo de los datos de la superficie. Es el vector de píxeles que constituye la superficie. La razón de que sea un puntero void es que no existe la manera estándar de representar los datos de pixel.
  • clip_rect: Estructura que indica el área rectangular y sólo rectangular de clipping de la superficie, que es el área dónde podemos realizar blit en dicha superficie. Las técnicas de clipping y de blitting serán explicadas con posterioridad.
  • refcount: Usado cuando se libera la superficie, es un contador de referencia. Cuando la superficie se crea toma el valor 1, y cuando es liberada se decrementa dicho valor en una unidad. Si existe un número mayor de 1 de superficies que depende de ésta podemos incrementar este valor tantas veces como dependencias existan lo que provoca que no se libere hasta que no quede ninguna dependencia pendiente.

Creando superficies o imagenes

Básicamente hay dos formas de crear una nueva Superficie o Imagen, la primera y más evidente es cargar una imagen "BMP", "PNG" o "JPG" con cualquiera de las funciones encargadas para esto como ejemplo: "SDL_Surface *imagen = IMG_Load( const char *nombre_archivo );". Y la que vamos a ver ahora que es creando una superficie nueva vacía. La manera de crear una nueva superficie es utilizando la función SDL_CreateRGBSurface(). Esta función nos permite crear una nueva superficie RGB vacía lista para ser usada. El prototipo de la función es:

SDL_Surface *SDL_CreateRGBSurface( Uint32 flags, int width, int height, int depth, Uint32 Rmask, Uint32 Gmask, Uint32 Bmask, Uint32 Amask );

Puedes observar como la función devuelve un puntero a una superficie que es, al fin y al cabo, lo que queremos obtener. Los parámetros que recibe la función están íntimamente relacionados con los de la estructura SDL y tienen prácticamente la misma semántica que estos. Es lógico ya que van a crear una estructura de este tipo. Pasamos a estudiar estos parámetros:

  • flags: Son unas banderas que indican las características de la superficie en cuestión, como donde se crea o si tiene habilitado el doble búffer.
  • int width: Anchura de la superficie a crear.
  • int height: Altura de la superficie a crear.
  • int depth: Profundidad de color. Igual que bpp en SetVideoMode.
  • Uint32 Rmask: Entero de 32 bits sin signo para la máscara roja.
  • Uint32 Gmask: Entero de 32 bits sin signo para la máscara verde.
  • Uint32 Bmask: Entero de 32 bits sin signo para la máscara azul.
  • Uint32 Amask: Entero de 32 bits sin signo para la máscara alfa o de transparencia.

Para comprender los parámetros Rmask, Gmask, Bmask y Amask debemos estudiar las estructuras internas de SDL. Estos parámetros pertenecen a la estructura SDL_PixelFormat. Cada parámetro de los comentados representa a la cantidad de color correspondiente que queremos añadir como si de una mezcla RGB se tratase. Los valores que puede tomar el campo flags de esta estructura son los siguientes:

  • SDL_SWSURFACE: Crea la superficie de vídeo en memoria principal. Esta es la mejor opción si vamos a modificar una superficie "a mano". El procesador tiene acceso directo a esta memoria por lo que puede escribir y leer directamente sobre ella.
  • SDL_HWSURFACE: Crea la superficie en la memoria de vídeo. El procesador no tiene acceso directo a esta memoria por lo que no podremos modificar o leer los píxeles de una superficie creada en esta memoria.
  • SDL_SRCCOLORKEY: Permite el uso de transparencias de color de base mediante la definición de un color clave (Color Key).
  • SDL_SRCALPHA: Activa el alpha blending.

A la hora de elegir en que memoria crear la superficie debemos de tener en cuenta los siguientes aspectos. En la memoria del sistema tenemos un acceso más rápido a los píxeles pero conseguimos una menor rapidez en el blitting. En la memoria de vídeo ocurre lo contrario, tenemos un acceso lento a los píxeles pero un mayor rendimiento a la hora de realizar blitting.

Ejemplo de Función:

SDL_Surface *Crea_Imagen_Color( int ancho, int alto, SDL_Color color ) {
 
    const SDL_VideoInfo *vi = SDL_GetVideoInfo();
    SDL_Surface *surface=SDL_CreateRGBSurface(
 
        SDL_SWSURFACE | SDL_SRCALPHA | SDL_SRCCOLORKEY,
        ancho, alto, vi->vfmt->BitsPerPixel,
        vi->vfmt->Rmask, vi->vfmt->Gmask,
        vi->vfmt->Bmask, vi->vfmt->Amask ); 
 
    SDL_SetColorKey( surface, SDL_SRCCOLORKEY|SDL_RLEACCEL,
    SDL_MapRGB( surface->format, color.r, color.g, color.b));
    return surface; 
 
}

SDL_SetColorKey es una función que define un color transparente en la imagen. Al crear los tiles para un juego se define un color que será el que se colocará como transparente. Por ejemplo una nave se le coloca el fondo verde y con esta función se elimina ese color. La estructura de la función es la siguiente:

int SDL_SetColorKey(SDL_Surface *surface, Uint32 flag, Uint32 key);

El primer parámetro es la superficie a la cual queremos aplicar el color transparente, el segundo son los flags, y el tercer es el color. Para ello utilizaremos la función SDL_MapRGB que nos devuelve un valor Uint32 con el valor de un color RGB.

A continuación vemos un ejemplo:

    Uint32 color; // Especificamos el verde como color para transparente
    color = SDL_MapRGB( imagen->format, 0,255,0 );
    SDL_SetColorKey( imagen, SDL_SRCCOLORKEY | SDL_RLEACCEL, color ); 

// Carga una imagen con un color transparente

// Puede manejar ficheros en formato BMP, PNM, XPM, LBM, PCX, GIF, JPG, PNG y TGA.
 
    SDL_Surface *Carga_Imagen_Color( const char *nombre_archivo, SDL_Color color ) {
 
        SDL_Surface *tmp, *bmp;
        tmp = IMG_Load( nombre_archivo );
        if( !tmp ) {
 
            printf("No se pudo cargar %s \n", nombre_archivo);
            exit(1); 
 
        }
        SDL_SetColorKey( tmp, SDL_SRCCOLORKEY | SDL_RLEACCEL,
 
            SDL_MapRGB( tmp->format, color.r, color.g, color.b ) ); 
 
        bmp = SDL_DisplayFormat( tmp );
        SDL_FreeSurface( tmp );
        if( !bmp ) {
 
            printf("No se pudo DisplayFormat %s \n", nombre_archivo);
            exit(1); 
 
        }
        return bmp;
 
    } 

Transparencias

int SDL_SetAlpha( SDL_Surface *surface, Uint32 flag, Unit8 alpha );

Puedes observar que la función es muy parecida a SDL_SetColorKey() ya que no deja de tener un cometido parecido. En este caso el parámetro flag puede tomar los valores 0, para desactivar el alpha o SDL_SRCALPHA para indicar que el tercer parámetro de la función es el alpha o transparencia que queremos aplicar a la superfice surface. Junto a SDL_SRCALPHA podemos activar SDL_RLEACCEL para la aceleración comentada en el subaparatado anterior. Los niveles de transparencia son 0 para totalmente opaco y 255 para totalmente transparente. Como las demás funciones devuelve 0 si se realizó la tarea con éxito y -1 en caso de error.

Pintando RECTÁNGULOS:

    int SDL_FillRect( SDL_Surface *dst, SDL_Rect *dstrect, Uint32 color );
 
// Pinta un Rectángulo
 
    void Pinta_Rectangulo_Lleno( SDL_Surface *imagen, int x, int y, int ancho, int alto, SDL_Color color ) {
 
        SDL_Rect dest = { x, y, ancho, alto };
        SDL_FillRect( imagen, &dest, SDL_MapRGBA( imagen->format, color.r, color.g, color.b, color.unused ) ); 
 
    }
 
// Limpia la superficie con un color
 
    void Limpia_Imagen( SDL_Surface *imagen, SDL_Color color ) {
 
        SDL_Rect dest = {0, 0, imagen->w, imagen->h};
        SDL_FillRect( imagen, &dest, SDL_MapRGBA( imagen->format, color.r, color.g, color.b, color.unused ) ); 
 
    }

BLITTING

Blitting es el proceso de hacer blit. Blit proviene de las palabras "block transfer" o en castellano, transferencia de bloques. Antiguamente la transferencia de bloques era abreviada como BLT. Como BLT no era pronunciable heredó una 'i' y de ahí surgió el término blit. Conceptualmente hacer blit puede ser equivalente al conocidísimo copy-paste aunque realmente hace más cosas que simplemente mover datos de una superficie a otra. Llamaremos blitter a la unidad de hardware o software que se encarga del blit y blitting a la acción de transferir bloques.

Para realizar un blit necesitas dos superficies, una de origen y una de destino, y dos rectángulos, también uno de origen y otro de destino. Una vez tengas esta información puedes realizar la llamada a la función de blitting entre superficies de SDL:

int SDL_BlitSurface(SDL_Surface *src, SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect);

 

 

Como puedes observar esta función recibe como parámetros toda la información que demandábamos antes, pasando todas las estructuras como punteros a las originales. Si los parámetros que definen los rectángulos de origen y de destino se inicializan con el valor NULL indicará que las superficies serán utilizadas enteras, ya que no se especifica rectángulo alguno. Debes saber que los rectángulos de origen y destino no tienen que coincidir ni en altura ni anchura, es más, el valor que tenga el rectángulo destino no influye en absoluto en la tarea de blitting, sólo importa las coordenadas donde queremos copiar la región. Esto es debido a que no es posible redimensionar una superficie al hacer blitting con la librería básica de SDL.

Esta función devuelve un entero. Si el valor de este entero es 0 todo habrá ido bien mientras qe si el valor devuelto es -1 indica que existe un error. Esta función devuelve un valor especial para describir una situación específica. Si la función devuelve el valor -2 indica que al menos una de las superficies están almacenadas en la memoria de vídeo por lo que necesitaremos refrescar para mostrar la imagen.

En el caso de realizar blitting entre dos superficies con formato de píxel diferentes la unidad de blitter de SDL se encarga de "traducir" un formato de píxel en otro. Este proceso es transparente al usuario pero supone una sobrecarga del sistema. Hay que tener en cuenta que ciertas operaciones de este estilo pueden afectar al rendimiento. La regla a tener en cuenta es bastante sencilla, a cuánto más complejidad, más lentitud. Cuantas más cosas obliguemos al blitter a realizar más tardará en realizarlas, así de simple. Una buena práctica es tener todas nuestras superficies en el mismo formato de píxel por lo que ahorraremos al blitter hacer traducciones.

Mostrando pantalla

// Muestra la Pantalla y espera el tiempo sobrante

    #define Frames_Segundo 20 // Son los fotogramas por segundo.
    void Muestra_Pantalla_Frame( void ) {
 
        SDL_Flip( pantalla );
        static const Uint32 frames = Uint32( 1000 / Frames_Segundo );
        // Son los fotogramas por segundo.
        static Uint32 tiempo = SDL_GetTicks() + frames ;
        // Se inicia la cuenta de tiempo.
        Uint32 t_espera = tiempo - SDL_GetTicks();
        if ( t_espera > 0 && t_espera <= frames ) SDL_Delay( t_espera );
        tiempo += frames; 
 
    }

Cerrando y liberando

// Elimina de la Memoria una Imagen

void Elimina_Imagen( SDL_Surface *imagen ) {
 
    SDL_FreeSurface( imagen ); 
 
}

// Cierra la libreria SDL

void Cierra_SDL( void ) {
 
    SDL_FreeSurface( pantalla );
    // Liberar memoria pantalla
    SDL_Quit(); // Cerrar SDL
    printf( "\nTodo ha salido bien.\n" ); 
 
}

EJEMPLO PRACTICO:

#include <cstdlib>
#include <iostream>
using namespace std;
#include "Mi_Libreria.h"
 
// -----------------------------------------------------------------------------
// Mis Clases, Programación Orientada a Objetos ( POO ) ------------------------
// -----------------------------------------------------------------------------
class sprite
{
private: // Variables y funciones privadas
SDL_Surface *imagen;
SDL_Rect rect;
int mas_x;
int mas_y;
public: // Variables y funciones públicas
sprite( const char *nombre_archivo ); // Constructor mismo nombre que la clase
sprite( const char *nombre_archivo, SDL_Color color );
~sprite( void ); // Destructor
void mostrar( void ); // Mostrar Sprite
};
 
sprite::sprite( const char *nombre_archivo ) // Constructor
{
imagen = Carga_Imagen_Alpha( nombre_archivo );
rect.x = rand() % ( Pantalla_X - 75 );
rect.y = rand() % ( Pantalla_Y - 75 );
rect.w = imagen->w;
rect.h = imagen->h;
do {
mas_x = ( rand() % 12 ) - 6;
mas_y = ( rand() % 12 ) - 6;
} while ( mas_x == 0 || mas_y == 0 );
}
 
sprite::sprite( const char *nombre_archivo, SDL_Color color ) // Constructor
{
imagen = Carga_Imagen_Color( nombre_archivo, color );
rect.x = rand() % ( Pantalla_X - 50 );
rect.y = rand() % ( Pantalla_Y - 50 );
rect.w = imagen->w;
rect.h = imagen->h;
do {
mas_x = ( rand() % 12 ) - 6;
mas_y = ( rand() % 12 ) - 6;
} while ( mas_x == 0 || mas_y == 0 );
}
 
sprite::~sprite( void ) // Destructor
{
Elimina_Imagen( imagen );
}
 
void sprite::mostrar(void) // Dibujamos el cursor del ratón
{
rect.x += mas_x;
rect.y += mas_y;
if ( rect.x < 0 || rect.x > Pantalla_X - imagen->w ) mas_x *= -1;
if ( rect.y < 0 || rect.y > Pantalla_Y - imagen->h ) mas_y *= -1;
SDL_BlitSurface( imagen, NULL, pantalla, &rect );
};
 
 
 
// -----------------------------------------------------------------------------
// MAIN FUNCION PRINCIPAL ------------------------------------------------------
// -----------------------------------------------------------------------------
int main(int argc, char *argv[])
{
 
// Inicimamos la Librerias SDL. ----------------------------------------------
Inicia_SDL( "Mi curso SDL Graficos." );
Icono_Ventana( ".\\Imagen\\mi_icono.png", (SDL_Color){255, 0, 255} );
// SDL_ShowCursor( SDL_DISABLE ); // No mostrar el cursor genérico windows.
SDL_ShowCursor( SDL_ENABLE ); // Mostrar el cursor genérico windows.
 
 
// Creamos colores. ----------------------------------------------------------
SDL_Color blanco = { 255, 255, 255 };
SDL_Color rojo = { 255, 0, 0 };
SDL_Color negro = { 0, 0, 0 };
SDL_Color verde = { 0, 255, 0 };
SDL_Color azul = { 0, 0, 255 };
 
 
// Inicializa la semilla de numeros aleatorios. ------------------------------
srand(time(0));
 
 
// Cargamos la imágen de fondo -----------------------------------------------
SDL_Surface *imagen_fondo = Carga_Imagen( ".\\Imagen\\fondo.png" );
SDL_Rect rect_fondo = { 0, 0, 100, 100 };
Sint16 fondo_mas_x = 4;
Sint16 fondo_mas_y = 4;
 
 
// Cargamos los Sprites ------------------------------------------------------
sprite cursor( ".\\Imagen\\cursor.png" );
sprite cursor_color( ".\\Imagen\\cursor.png", (SDL_Color){ 255, 0, 255} );
sprite copo( ".\\Imagen\\sprite.png" );
sprite copo_color( ".\\Imagen\\sprite.png", (SDL_Color){ 255, 0, 255} );
 
 
// Cargamos una Imagen para Rotar. -------------------------------------------
SDL_Surface *imagen_a_rotar = Carga_Imagen_Alpha( ".\\Imagen\\mail.png" );
SDL_Surface *imagen_rotada;
SDL_Rect rect_rotar;
float angulo = 0, zoom = 0;
 
 
// Creamos mi imagen ---------------------------------------------------------
SDL_Surface *mi_imagen = Crea_Imagen_Color( 50, 50, verde );
Limpia_Imagen( mi_imagen, verde );
// Pintamos Pixels en la imagen con color RGB.
Uint32 azul_RGB = Crea_Color_RGB( mi_imagen, (SDL_Color){ 0, 0, 255 } );
Bloquea_Imagen( mi_imagen ); // Bloquear Imagen para pintar Pixels.
for ( Sint16 p_x = 0; p_x < 50; p_x+= 2 )
{
for ( Sint16 p_y = 0; p_y < 50; p_y+= 2 )
{
Pinta_Pixel_Color_RGB( mi_imagen, p_x, p_y, azul_RGB );
}
}
Desbloquea_Imagen( mi_imagen ); // Desbloquear Imagen al terminar Pixels.
 
 
// Bucle Principal, usa variable externa llamada "terminar" ------------------
while( !terminar )
{
 
// Dibujamos el fondo ------------------------------------------------------
Limpia_Imagen( pantalla, negro );
SDL_SetAlpha( imagen_fondo, SDL_SRCALPHA|SDL_RLEACCEL, 127 );
SDL_BlitSurface( imagen_fondo, NULL, pantalla, &(SDL_Rect){0,0,0,0} );
rect_fondo.x += fondo_mas_x;
rect_fondo.y += fondo_mas_y;
rect_fondo.w = 100;
rect_fondo.h = 100;
if ( rect_fondo.x < 0 || rect_fondo.x > Pantalla_X - 100 ) fondo_mas_x *= -1;
if ( rect_fondo.y < 0 || rect_fondo.y > Pantalla_Y - 100 ) fondo_mas_y *= -1;
SDL_SetAlpha( imagen_fondo, SDL_SRCALPHA|SDL_RLEACCEL, 255 );
SDL_BlitSurface( imagen_fondo, &rect_fondo, pantalla, &rect_fondo );
 
 
 
// Pintamos Pixels ---------------------------------------------------------
Bloquea_Imagen( pantalla );
for ( Sint16 p_x = 0; p_x < Pantalla_X; p_x++ )
{
Pinta_Pixel_SDL_Color( pantalla, p_x, ( p_x * Pantalla_Y ) / Pantalla_X, azul );
}
Desbloquea_Imagen( pantalla );
 
 
// Pintamos unas primitivas. -----------------------------------------------
Pinta_Linea( pantalla, 0, 480, 640, 0, rojo );
 
Pinta_Rectangulo( pantalla, 100, 50, 200, 150, verde );
 
Pinta_Rectangulo_Lleno( pantalla, 500, 100, 50, 50, rojo );
 
Pinta_Circulo( pantalla, 320, 240, 110, rojo );
 
 
 
// Pintamos una Imagen Rotando y con Zoom. ---------------------------------
imagen_rotada = rotozoomSurface( imagen_a_rotar , angulo, zoom, 0);
++angulo;
zoom += 0.005;
if ( angulo >= 360 ) angulo = 0;
if ( zoom >= 2 ) zoom = 0;
rect_rotar.x = 200 - (imagen_rotada->w >> 1 );
rect_rotar.y = 300 - (imagen_rotada->h >> 1 );
rect_rotar.w = 0;
rect_rotar.h = 0;
SDL_BlitSurface( imagen_rotada, NULL, pantalla, &rect_rotar );
 
 
// Pintamos Mi imagen. -----------------------------------------------------
SDL_BlitSurface( mi_imagen, NULL, pantalla, &(SDL_Rect){ 500, 200, 0, 0} );
 
 
// Pintamos los Sprites. ---------------------------------------------------
cursor.mostrar();
cursor_color.mostrar();
copo.mostrar();
copo_color.mostrar();
 
 
// Esperamos Eventos y Mostramos la Pantalla con Frames_Segundo.------------
Espera_Eventos();
Muestra_Pantalla_Frame();
 
}
 
 
Elimina_Imagen( imagen_fondo ); // Liberar memoria.
Elimina_Imagen( mi_imagen ); // Liberar memoria.
Elimina_Imagen( imagen_a_rotar); // Liberar memoria.
Elimina_Imagen( imagen_rotada ); // Liberar memoria.
 
 
Cierra_SDL(); // Cierra las Librerias SDL.
 
return EXIT_SUCCESS;
 
}

Descarga el Ejemplo

Descargar Ejemplo  de Graficos  (Sólo usuarios registrados)

Descargar Ejemplo de Graficos mas los Alpha  (Sólo usuarios registrados)

4.625
Tu voto: Ninguno Votos totales: 4.6 (16 votos)

Anuncios Google

Comentarios

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.

muchisimas gracias no sabes

muchisimas gracias no sabes cuanto me has ayudado en esto del C

yo programo en Basic pero habia estado buscando como empezar en C

y todo esto me esta ayudando bastante     gracias...Risa

Imagen de P22

Oye, tengo un problema. Me

Oye, tengo un problema.

Me podrias decir donde esta la pestaña argum..?? No la encuentro.

A ser posible con una captura.

Saludos

 


¡Iníciate en Linux fácilmente! Sólo entra aquí y comprueba que distribución se adapta mejor a tí.

Mi review: iPod Touch 4G

Imagen de Unicorn

Vaya lujo

tener este tipo de tutoriales. Estoy seguro de que el dia de mañana daran sus frutos, como los han dado los de PSP (aunque han tenido que pasar meses para que "cuajen" de verdad).

Tienes mis 5 estrellas, como casi siempre pipa.

Un saludo compañero, y gracias por aportar tu granito de arena para difundir el arte de la programacion.


Para recibir ayuda más rápidamente, recomendamos que pongas títulos descriptivos y no utilices abreviaturas (estilo MSN) en tus post de los foros. Recuerda revisar el Manual del perfecto forero y las Normas de la Comunidad.

Imagen de pipagerardo

Gracias...

La programación en C++ es mucho más compleja que en LUA pero merece la pena por la versatilidad y la potencia.

Yo me he dado 6 meses para estudiar C++ y las Librerias SDL, así que es normal que mis tutoriales en LUA tarden en dar resultados, nadie se lee un tutorial y hace un juego a la primera. Gracias Unicorn...

¿Tienes idea de hacer algo

¿Tienes idea de hacer algo en la psp con el c++?, lo digo mas q nada por "mantenerte" dentro de los limites de psp, usar la resolucion 480x272, tratar de evitar los png con canal alpha 32bits, evitar en la medida de lo posible sdl_alpha y evitar cualquier libreria como la sdl_gfx pues tiene una gran perdida de rendimiento.

A ver si un dia de estos me animo yo a poner mi codigo en c++, saludos

Opciones de visualización de comentarios

Seleccione la forma que prefiera para mostrar los comentarios y haga clic en «Guardar las opciones» para activar los cambios.