.:: Menu Rápido ::.

Linux | C/C++ | Downloads | SDL | [×]

quinta-feira, 7 de fevereiro de 2008

SDL_Surface - Introdução detalhada

Este post deveria ter vindo logo depois do primeiro sobre SDL (SDL - A Biblioteca dos Jogos 2D). Se você estava meio perdido esta leitura é recomendada.
A abordagem começa com os pixels e se estende até o blit completo ou parcial.

» Post Completo...

typedef struct SDL_Surface {
Uint32 flags;
SDL_PixelFormat *format;
int w, h;
Uint16 pitch;
void *pixels;

SDL_Rect clip_rect;
int refcount;
} SDL_Surface;

Os parêmetros mais interessantes são w (largura da imagem), h (altura da imagem), pitch (bytes de cada linha) e pixels (ponteiro para o vetor de bytes).
Todos estes valores são apenas para leitura, exceto pixels. Nem sempre pixels está disponível para escrita, neste caso você deve usar SDL_LockSurface() e SDL_UnlockSurface() para ter acesso direto aos dados.

Uma curiosidade é saber como os pixels estão dispostos exatamente em *pixels. Uma imagem 3x3 de 24bpp possui pitch=12 e pixels[36]. w*h*bpp = 3*3*3 = 27, mas não é dessa forma que as coisas funcionam.
Observe a imagem:
Pode existir lixo no final de cada linha quando ela não for multipla de 32 bits (4 bytes), mas isto não é um bug, é apenas uma otimização. Logo o cálculo fica: pitch*h = 36

*** Screen ***
É através da SDL_Surface que tudo será apresentado ao usuário, e usualmente eu nomeio a superfície da tela como screen:

#include <SDL/SDL.h>

SDL_Surface *screen = NULL; //- Tela

int main(int argc, char *argv[]){
//- Inicializa a SDL -//
SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER);
//- Inicializa a tela -//
screen = SDL_SetVideoMode(640,480,32,SDL_SWSURFACE);
//- título da janela -//
SDL_WM_SetCaption("Aplicação SDL em 3 segundos !!!", NULL);

//- Atualiza a tela -//
SDL_Flip(screen);
//- Pausa de 3 segundos -//
SDL_Delay(3000);

//- Finaliza a SDL -//
SDL_Quit();
return 0;
}

Explicando melhor cada linha do exemplo:
» Incluí a biblioteca SDL. (Ela deve estar instalada)
» Criei um ponteiro para armazenar a superfície da tela (screen).
» Minha função main da maneira correta, exatamente como a SDL a espera.
» Inicializo a SDL
» Inicializo a tela. 640x480 e 32bpp é criada na memória.
» Define "Aplicação SDL em 3 segundos !!!" como título da janela.
» Mostra a superfície no vídeo. Se você estiver utilizando SDL_DOUBLEBUF esta função terá o papel crucial.
» Como ainda não estamos tratando eventos do teclado fiz uma pausa de 3 segundos para que a janela possa ser visualizada.
» Devemos inicializar e também finalizar a SDL.
» Retornar 0 indica que tudo ocorreu bem.

*** Imagens ***
Com SDL é fácil carregar (SDL_LoadBMP) e salvar (SDL_SaveBMP) imagens no formato BMP. Pena que o formato BMP não suporta mais que 24bpp, e isto nos limita a usar cor de transparência, já que não podemos carregar imagens com alpha. O formato PNG suporta alpha, mas não se pode carregar PNG com a função SDL_LoadBMP(), para isto devemos utilizar a lib SDL_Image (veremos mais à frente).
Trecho de código:

SDL_Surface *img = NULL;
...
int main(int argc, char *argv[]){
//- Inicializa a SDL -//
...

//- carrega imagem -//
img = SDL_LoadBMP("img/imagem.bmp");

//- pinta imagem na em outra screen -//
SDL_BlitSurface(img,0,screen,0);

//- salvando a tela -//
SDL_SaveBMP(screen,"img/screen.bmp");

//- Atualiza a tela -//
SDL_Flip(screen);
//- Pausa de 3 segundos -//
SDL_Delay(3000);

//- limpando memória -//
SDL_FreeSurface( img );

//- Finaliza a SDL -//
SDL_Quit();
return 0;
}

» Um ponteiro SDL_Surface para armazenar nossa imagem.
» Em main inicializamos a SDL.
» Carregamos "img/imagem.bmp" com a função SDL_LoadBMP e guardamos seu ponteiro em img. Usar / é o mesmo que usar \\ e funciona no Windows e Linux. Caso a imagem não seja carregada verifique se seu nome está correto ou se o programa está sendo executado do local correto. system("pwd") ou system("dir") podem esclarecer o que digo.
» SDL_BlitSurface é capaz de pintar uma superfície sobre outra. Desta maneira a imagem será pintada completamente nas coordenadas 0x0 da imagem de destino.
» Com SDL_SaveBMP é fácil gravar uma screenshot do seu jogo !
» SDL_Flip e SDL_Delay para visualizarmos a janela.
» SDL_FreeSurface é utilizada para limpar a memória da imagem carregada.
» Por fim SDL_Quit e return 0;

Se você quiser ganhar performance em seu jogo não trabalhe com diferentes formatos de superfície (não estou falando dos formatos de arquivo). Toda vez que blita uma superfície de 24bpp em outra de 32bpp há uma conversão em tempo de blitagem, e para ganhar performance temos que deixar todas as superfícies convertidas em um único formato, o formato do display (nossa tela).

SDL_Surface *aux, *img;
aux = SDL_LoadBMP("smiley.bmp");
SDL_SetColorKey(aux, SDL_SRCCOLORKEY, SDL_MapRGB(aux->format,0xff,0,0xff) );
img = SDL_DisplayFormatAlpha(aux);
SDL_FreeSurface(aux);

» aux recebe uma imagem de 24bpp.
» O rosa 0xff00ff é setado como cor de transparência. (faça antes de converter pro display)
» SDL_DisplayFormatAlpha é chamado e converte a imagem para 32bpp e transforma a cor de transparência em uma camada de transparência.
» aux deve ser limpada, pois SDL_DisplayFormat cria uma nova superfície.

*** SDL_BlitSurface ***
Até agora só trabalhamos com SDL_BlitSurface em sua forma mais simples (imagem, NULL, imagem, NULL). Esta função tem diversas utilidades, tais como imprimir apenas parte de uma imagem em outra.
Analisando cuidadosamente temos:

int SDL_BlitSurface(
SDL_Surface *src, //- Imagem de origem (imagem a pintar)
SDL_Rect *srcrect, //- Retângulo de corte. permite pintar
// parte dela.
SDL_Surface *dst, //- Imagem de destino (imagem modificada)
SDL_Rect *dstrect //- Coordenadas da imagem de origem.
);

Explicarei com vários exemplos.
A forma mais simples de uso é onde uma imagem é toda impressa em outra nas coordenadas de origem:

SDL_BlitSurface(img, NULL, screen, NULL);

Para imprimir uma imagem por completo em uma determinada posição de outra imagem:

SDL_Rect xy = {x,y,0,0};
SDL_BlitSurface(img, NULL, screen, &xy);

Para imprimir parte de uma imagem em uma determinada posição de outra imagem:

SDL_Rect ret = {retx, rety, retw, reth};
SDL_Rect xy = {x,y,0,0};
SDL_BlitSurface(img, &ret, screen, &xy);

Mais informações:
» Uma imagem não precisa estar realmente cortada para que se possa imprimir parte dela.
» A imagem de destino nunca irá mudar de tamanho para acomodar a imagem de origem.
» Só serão aproveitados o x e y do retângulo de destino, logo ele só servirá para posicionar onde a imagem será impressa.
» Você não poderá redimensionar ou rotacionar imagens com SDL_BlitSurface. Para isto use SDL_Gfx.

*** Link's ***
Download: » código fonte
» Surface Loading and Blitting
» Documentação da SDL (online)

5 comentários:

Anônimo disse...

aew cara!!
mtoo bom o blog! ja ta add no favoritos!

continuee assim :D!!

t++ abrasss

Diogo_RBG disse...

Vlw !
O objetivo é não quebrar a corrente... não cheguei aqui sozinho e agradeço a todos !

Anônimo disse...

Diogo,

Somos 2 desenvolvedores e estamos procurando mais gente que goste de criacao de jogos e tenha experiencia na area.

Estamos comecando a rascunhar um jogo no estilo tactics (FF Tactics, Ogre Tactics) pra Linux/Windows.

Caso voce esteja interessado, manda um email pra renato.azimuth@gmail.com e conversaremos.

Anônimo disse...

Entender a forma com que a SDL representa suas superficies é fundamental, ajuda a achar possiveis bugs, e criar funções para manipular bit-a-bit. O que mais gostei no seu artigo foi o Mais informações isso ajuda muito quem está começando, pois as vezes ficamos confuzos se devemos ou não usar algo.
Parabéms!

Anônimo disse...

fla primao>>
tou de olho por aqui>>
abrçao..
jonathas