[LinuxFocus-icon]
<--  | Hogar  | Mapa  | Indice  | Busqueda

Noticias | Arca | Enlaces | Sobre LF
Este documento está disponible en los siguientes idiomas: English  Castellano  Deutsch  Francais  Nederlands  Turkce  

[Leonardo]
por Leonardo Giordani
<leo.giordani(at)libero.it>

Sobre el autor:

He obtenido mi diploma por parte de la Facultad de Ingeniería en Telecomunicaciones del Politécnico de Milán. Estoy interesado en la programación, principalmente en ensamblador y C/C++. Trabajo desde 1999 con Linux y Unix solamente.



Traducido al español por:
Edgar Hernández Zúñiga <edgar(at)linuxfocus.org>

Contenidos:

 

Implementando un desplazador de texto en gráficos SDL

[sdl]

Resumen:

Esta serie de artículos tiene el propósito de introducir al lector en el mundo de las producciones multimedia, también conocidas como "demos". Toda la Internet está llena de información en la materia, pero algunas personas han conseguido grandes cosas en Linux: my meta es describir la teoría de algunos efectos gráficos y de audio y su implementación utilizando la biblioteca SDL. Podrán encontrar más información en:

  • Simple DirectMedia Layer: leer el código de las muestras y los juegos es la mejor manera de aprender nuevas soluciones.
  • lnxscene: un sitio nuevo, pero un gran lugar para el intercambio de conocimiento; podrás encontrarme algunas veces como "muaddib"

Los requisitos para el entendimiento del artículo son:

  • Conocimiento básico del lenguaje C, sintáxis, ciclos, bibliotecas.
  • Conocimiento básico de la biblioteca SDL, funciones básucas e inicialización., te servirá consultar www.libsdl.org

_________________ _________________ _________________

 

El desplazador o voluta

Agradezco ampliamente a Sam Lantinga por la biblioteca SDL.

Un desplazador o voluta, es parte de un "demo" que permite escribir una sentencia o texto en movimiento en la panatalla o monitor: este es uno de los efectos básicos que puedes encontrar en cualquier producción multimedia al cual es posible añadirle un poco de dinámica al mostrársele al usuario. En éste artículo veremos como construir un desplazador básico que permitirá mover texto de derecha a izquierda.

La idea básica de un desplazador, es copiar una parte de la pantalla uno o más pixeles hacia la izquierda o en alguna otra dirección. Ejecutando esta operación con una buena velocidad obtendrás la ilusión de un movimiento continuo, eso es todo.

La teoría básica no es nada complicada; vamos a traducir todo esto en código: nos referiremos en adelante al concepto de superficie, dado que tenemos ya los conocimientos básicos de programación con SDL. Mientras trabajemos con la biblioteca SDL, debemos recordar siempre que podemos usar el poderío de la función

SDL_BlitSurface() la cual nos permite hacer una copia de una parte de una SDL_Surface identificada por una SDL_Rect en otra SDL_Surface identificada por otra SDL_Rect .

Por ejemplo, imaginemos que hemos definido e inicializado dos superficies y dos rectángulos

#define WIDTH 800
#define HEIGHT 600

SDL_Surface *Src;
SDL_Surface *Dst;

Src = SDL_CreateRGBSurface(SDL_HWSURFACE, WIDTH, HEIGHT, 8,
       r_mask, g_mask, b_mask, a_mask);
Dst = SDL_CreateRGBSurface(SDL_HWSURFACE, WIDTH, HEIGHT, 8,
       r_mask, g_mask, b_mask, a_mask);

SDL_Rect BigArea = {0, 0, (WIDTH / 2), HEIGHT};
SDL_Rect SmallArea = {(WIDTH / 2) + 1, 0, (WIDTH / 2), (HEIGHT / 2)};
Suponiendo que el color de la máscara se encuentra correctamente inicializado. Copiar las dos superficies completas implica solo un pequeño esfuerzo.
SDL_BlitSurface(Src, 0, Dst, 0);
Nota que en el destino de la superficie solo las coordenadas del rectángulo están consideradas y no su dimensión, esto significa que la operación
SDL_BlitSurface(Src, &BigArea, Dst, &SmallArea);
es perfectamente legal y copia la mitad izquierda de Src la superficie en la mitad derecha de la Dst superficie.

Ahora ejecutar el desplazado de una superficie no es más un misterio: es suficiente copiar una parte de la superficie en un rectángulo cambiado en la misma superficie; es obvio que esto tendremos que crearlo como un ciclo, con esto, el programa resultante será un poco más complejo, pero el concepto básico se mantiene simple. A cada instante del ciclo usaremos dos rectángulos, el segundo cambio se refiere al primero en la dirección en la que se ha determinado el desplazamiento, y copiaremos la superficie a sí misma del primero al segundo rectángulo.

SDL_Surface *temp;

SDL_Rect From = {1, 0, WIDTH, HEIGHT};
SDL_Rect First = {0, 0, 1, HEIGHT};
SDL_Rect Last = {WIDTH-1, 0, 1, HEIGHT};

temp = SDL_CreateRGBSurface(SDL_HWSURFACE, 1, HEIGHT, 8,
       r_mask, g_mask, b_mask, a_mask);

while(1){
  SDL_BlitSurface(Src, &First, temp, 0);
  SDL_BlitSurface(Src, &From, Src, 0);
  SDL_BlitSurface(temp, &First, Src, &Last);
  SDL_BlitSurface(Src, 0, Screen, 0);
}
Como puedes ver, esto no es suficiente para desplazar la superficie hacia la izquierda: tenemos que reinsertar desde la derecha los pixeles que han salido de la pantalla, o la superficie de desplazamiento estará detrás de las copias generando un pequeño efecto de "deslizamiento". Con esto suponemos que está lista una superficie vinculada en la pantalla. Ahora veamos un programa completo que abre una ventana de 480x200 pixeles y muestra el desplazamiento continuo de una imagen en ella.
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"

#define SCREEN_WIDTH 480
#define SCREEN_HEIGHT 200

#if SDL_BYTEORDER == SDL_BIG_ENDIAN
static const Uint32 r_mask = 0xFF000000;
static const Uint32 g_mask = 0x00FF0000;
static const Uint32 b_mask = 0x0000FF00;
static const Uint32 a_mask = 0x000000FF;
#else
static const Uint32 r_mask = 0x000000FF;
static const Uint32 g_mask = 0x0000FF00;
static const Uint32 b_mask = 0x00FF0000;
static const Uint32 a_mask = 0xFF000000;
#endif
Este grupo de definiciones es estándar en la mayoría de las producciones multimedia.
static SDL_Surface* img_surface;
static SDL_Surface* scroll_surface;
static SDL_Surface* temp_surface;
Aquí están las tres superficies que usaremos: img_surface contendrá la imagen obtenida de un archivo, scroll_surface la imagen cambiada y, temp_surface los pixeles que hemos obtenido nuevamente de la derecha.
static const SDL_VideoInfo* info = 0;
SDL_Surface* screen;
Una SDL_VideoInfo estructura, contiene información acerca de la tarjeta de video, con la cual screen la superficie permite apuntar hacia la pantalla o monitor.
int init_video()
{
  if( SDL_Init( SDL_INIT_VIDEO) < 0 )
    {
      fprintf( stderr, "Video initialization failed: %s\n",
               SDL_GetError( ) );
      return 0;
    }

  info = SDL_GetVideoInfo( );

  if( !info ) {
    fprintf( stderr, "Video query failed: %s\n",
             SDL_GetError( ) );
    return 0;
  }

  return 1;
}

int set_video( Uint16 width, Uint16 height, int bpp, int flags)
{
  if (init_video())
    {
      if((screen = SDL_SetVideoMode(width,height,bpp,flags))==0)
        {
          fprintf( stderr, "Video mode set failed: %s\n",
                   SDL_GetError( ) );
          return 0;
        }
    }
  return 1;
}
La función init_video() inicializa el subsistema de video SDL y llena completamente la info estructura. La funci set_video() intenta establecer la proporción o modo de video que fue establecido, tamaño y profundidad de color.
void quit( int code )
{
  SDL_FreeSurface(scroll_surface);
  SDL_FreeSurface(img_surface);

  SDL_Quit( );

  exit( code );
}
Esta es la función esencial de terminación, la cual libera todos los recursos que utilizamos así como las llamadas SDL_Quit .
void handle_key_down( SDL_keysym* keysym )
{
    switch( keysym->sym )
      {
      case SDLK_ESCAPE:
        quit( 0 );
        break;
      default:
        break;
      }
}
Un evento "Presionar tecla": en este caso la tecla ESC.
void process_events( void )
{
    SDL_Event event;

    while( SDL_PollEvent( &event ) ) {

        switch( event.type ) {
        case SDL_KEYDOWN:
            handle_key_down( &event.key.keysym );
            break;
        case SDL_QUIT:
            quit( 0 );
            break;
        }
    }
}
La función para el control de un evento no menor.
void init()
{
  SDL_Surface* tmp;
  Uint16 i;

  tmp = SDL_CreateRGBSurface(SDL_HWSURFACE, SCREEN_WIDTH,
       SCREEN_HEIGHT, 8, r_mask, g_mask, b_mask, a_mask);

  scroll_surface = SDL_DisplayFormat(tmp);


  SDL_FreeSurface(tmp);
Vamos a trabajar con tmp una superficie para inicializar scroll_surface y temp_surface Ambas son convertidas en formato de video framebuffer por la funci SDL_DisplayFormat
  img_surface = IMG_Load("img.pcx");
Aquí cargaremos dentro de img_surface la imagen guardada en el archivo.
  for (i = 0; i < SDL_NUMEVENTS; ++i)
    {
      if (i != SDL_KEYDOWN && i != SDL_QUIT)
        {
          SDL_EventState(i, SDL_IGNORE);
        }
    }

  SDL_ShowCursor(SDL_DISABLE);
}
Todos los eventos son ignorados, a excepción de la tecla ESC que teminará el programa; además de eso, desactivaremos el puntero. All events are ignored except for the pressure of a key and the exit from the program; moreover we disable the cursor.
int main( int argc, char* argv[] )
{
  SDL_Rect ScrollFrom = {1, 0, SCREEN_WIDTH, SCREEN_HEIGHT};
  SDL_Rect First = {0, 0, 1, SCREEN_HEIGHT};
  SDL_Rect Last = {SCREEN_WIDTH - 1, 0, 1, SCREEN_HEIGHT};
Estos son los tres rectángulos descritos en el artículo.cle.
  if (!set_video(SCREEN_WIDTH, SCREEN_HEIGHT, 8,
      SDL_HWSURFACE | SDL_HWACCEL | SDL_HWPALETTE /*|
SDL_FULLSCREEN*/))
   quit(1);

  SDL_WM_SetCaption("Demo", "");

  init();

  SDL_BlitSurface(img_surface, 0, scroll_surface, 0);
Este código inicializa todo lo necesario: establece el modo de video escribe el título de la ventana, llama a init() y prepara scroll_surface .
  while( 1 )
    {
      process_events();

      SDL_BlitSurface(scroll_surface, &First, temp_surface, 0);

      SDL_BlitSurface(scroll_surface, &ScrollFrom,
scroll_surface, 0);

      SDL_BlitSurface(temp_surface, &First, scroll_surface,
&Last);

      SDL_BlitSurface(scroll_surface, 0, screen, 0);

      SDL_Flip(screen);
    }

  return 0;
}
Este es el ciclo descrito en el artículo: solo el evento para controlar las funciones y el regreso de la superficie en la pantalla han sido añadidos.

Como puedes ver, el trabajo de inicialización de la biblioteca no es pequeño, pero la ventaja es que es común para todo el "demo", así, cuando el código incrementa, la inicialización continua siendo una pequeña parte de todo el programa, esto lo hace portable y reutilizable.

 

Una pequeña demostración

Aquí está una captura de pantalla en diferentes momentos:
state1 state2

PD: Puedes hacerme llegar cualquier comentario, corrección o pregunta a mi dirección de correo o a través de la página de Retroalimentación, Talback. Escríbeme en inglés, alemán o italiano.


<--, regresar al índice de este número

Contactar con el equipo de LinuFocus
© Leonardo Giordani, FDL
LinuxFocus.org
Información sobre la traducción:
it --> -- : Leonardo Giordani <leo.giordani(at)libero.it>
it --> en: Leonardo Giordani <leo.giordani(at)libero.it>
en --> es: Edgar Hernández Zúñiga <edgar(at)linuxfocus.org>

2003-07-02, generated by lfparser version 2.40