.:: Menu Rápido ::.

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

quinta-feira, 27 de março de 2008

SDL em Processamento de Imagens

Depois de muito tempo sem postar, estou de volta.
Desta vez não vou falar de teoria ou documentação, mas apenas de um exemplo prático de como utilizar SDL em Processamento de Imagens.

SDL não tem componentes gráficas, caixa de diálogo ou funções de acesso ao pixel, mas a gente pode improvisar.
Em SDL trabalhar com pixels é muito fácil e rápido.
Imagem 2D é com a SDL mesmo !

Download: » código fonte

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)

sábado, 26 de janeiro de 2008

Script's GIMP - Python-Fu !!!

Calma... Python-Fu não é um novo estilo ninja !!!
Não é atoa que o GIMP é a melhor ferramenta gráfica 2D e open source da atualidade. Ela é perfeita para ser manipulada por artistas e programadores.
Saiba como automatizar aquele efeito especial que você geralmente usa ou criar algum que só é possível através de programação !

» Post Completo...

quarta-feira, 9 de janeiro de 2008

Imprimindo texto com SDL_Ttf

Você já deve ter reparado que a função printf não faz milagres quando se está utilizando SDL. Isso é normal, pois a superfície da SDL não é uma saída padrão. A saída padrão é o console (Linux) ou o arquivo stdout.txt (Windows).
A única maneira de se imprimir texto na superfície da SDL é desenhando letra por letra, mas isso não significa que você tenha que fazer isso na unha !
A forma mais simples de resolver este problema é utilizando a lib SDL_Ttf (SDL True Type Fonts).
Além de mostrar minhas soluções para entrada e saída de texto também abordo um pouco sobre manipulação de argumentos variáveis.

» Post Completo...

int TTF_Init();
void TTF_Quit();
char *TTF_GetError();
TTF_Font *TTF_OpenFont(const char *file, int ptsize);
void TTF_CloseFont(TTF_Font *font);
int TTF_SizeText(TTF_Font *font, const char *text, int *w, int *h)
SDL_Surface *TTF_RenderText_Solid(TTF_Font *font, const char *text, SDL_Color fg);
...


É importante saber que é preciso inicializar (TTF_Init) e encerrar (TTF_Quit) a biblioteca e também carregar (TTF_OpenFont) e descarregar (TTF_CloseFont) o recurso TTF_Font.

A parte de renderização (impressão de texto) possui alguns conceitos a serem estudados:
» Solid: Rápido e direto. Cria uma nova camada de 8bpp (usa paleta) com o texto impresso em baixa qualidade (2 cores). 0 é o colorkey e 1 é a cor da font. O resultado é rápido, o texto tem cor de transparência mas é serrilhado.
» Shaded: Lento com caixa de cor sólida. Cria uma nova camada de 8bpp (usa paleta) com o texto impresso em alta qualidade (com anti-aliasing). O resultado não é muito rápido, o texto não é serrilhado mas também não tem cor de transparência.
» Blended: Muito lento e com camada de transparência. Cria uma nova camada de 32bpp (usa camada alpha) com o texto impresso em alta qualidade (com anti-aliasing). É o resultado mais lento, o texto não é serrilhado e tem transparência.

A lib SDL_Ttf permite vários tipos de codificação de texto: LATIN1, UTF8, UNICODE text/glyph.
Isto significa que você não terá problemas com acentuação, como é comum acontecer quando você escreve seu código em ISO-8859-1 e o programa imprime num terminal IBM-850.
UTF-8 é a codificação de tamanho variável que pode representar qualquer caracter universal padrão do Unicode, mas para nós do ocidente o LATIN1 é mais do que suficiente.

Um exemplo rápido de como utilizar a lib SDL_Ttf: (Não compile isto !)

TTF_Font *font_16 = NULL;
SDL_Surface *screen=NULL, *aux=NULL;
...
int TTF_Init();
...
font_16 = TTF_OpenFont("free_serif.ttf", 16);
...
SDL_Color cor = {255,255,255,0};
aux = TTF_RenderText_Solid(font_16, "Texto !!!", cor );
SDL_BlitSurface(aux, NULL, screen, NULL);
SDL_FreeSurface(aux);
...
TTF_CloseFont(font_16);
...
void TTF_Quit();


*** O Exemplo ***
No exemplo você vai encontrar os 3 tipos de renderização, um exemplo de texto formatado e um exemplo de leitura de texto.
Download: » código fonte

*** Impressão de texto formatado ***
A função printf sempre foi uma mão na roda, pois ela formata, converte e imprime (Tudo ao mesmo tempo). E o que dizer da quantidade variável de argumentos ?! Ela sempre foi um verdadeiro canivete suíço !!!
Não podemos imprimir direto na tela, mas podemos imprimir para um vetor com sprintf. E melhor ainda, criar nossa função print(x,y,texto,...) com argumentos variáveis com a ajuda da função vsprintf:

void print(int x,int y,const char *format, ...){
SDL_Surface *aux;
SDL_Color cor = {255,255,255,0};
SDL_Rect ret={x,y,0,0};

//- Imprimindo para vetor -//
char buf[256];
va_list args;
va_start (args, format);
vsprintf (buf,format, args);
va_end(args);

aux = TTF_RenderText_Blended(font_16, buf, cor);
SDL_BlitSurface(aux,0,screen,&ret);
SDL_FreeSurface(aux);
}

Mais explicações logo abaixo !!!

*** Manipulação de argumentos variáveis ***
Já se perguntou como como é possível declarar: int printf( const char *format, ... ) ?
Samuel Dias Neto: "Uma função pode ser chamada com um número variável de argumentos sendo estes argumentos de diversos tipos. O arquivo de cabeçalho stdarg.h declara um tipo va_list e define três macros (va_start, va_arg e va_end) para manipular uma lista de argumentos cuja quantidade e tipos são desconhecidos pela função."
» va_list: tipo para a listas de argumentos variável.
» va_start: inicializa a listas de argumentos variável.
» va_arg: retira o próximo argumento.
» va_end: finaliza a listas de argumentos variável.

*** Leitura de texto ***
Já sabemos como escrever, agora só falta saber como ler ! Aqui é a parte em que eu tive que improvisar, pois não existe a função SDL_Scanf !!!
Minha função de leitura foi feita para teclados ABNT2 e não aceita acentuação, mas pode quebrar um galho:

std::string str;
bool shift;
void scan(SDLKey key, bool shift){
...
if(key==SDLK_BACKSPACE && !str.empty())
str.erase(str.length()-1);
else if(key==' ')
str += key;
else if(key>=SDLK_KP0 && key<=SDLK_KP9)
str += key - SDLK_KP0 + '0';
else if(key>='a' && key<='z'){
if(shift)
str += key-'a'+'A';
else
str += key;
}else{
...
}
}


*** Link's ***
Download: » código fonte
» Site SDL_Ttf
» LazyFoo (True Type Fonts)
» SDL - A Biblioteca dos Jogos 2D

terça-feira, 8 de janeiro de 2008

DR Tetris 0.3 - Tetris no estilo tradicional !

2007 foi um ótimo ano, mas 2008 promete ! E para começar o ano com o pé direito estou lançando a terceira edição do meu jogo de tetris.
DR Tetris 0.3 é um jogo de tetris estilo tradicional que promete matar saudade dos velhos tempo em que se perdiam horas em mini games (Eu sou um exemplo !)


Algumas das características são:
* Roda em Windows e Linux.
* Sons do jogo original (Ou quase !).
* Sombra projetando onde a peça irá cair.
* Placar de recordes com 13 posições (Editável).
* Acumulador de pontos em sequência.
* Níveis diferentes de velocidade.

Você pode baixar no site do jogo: DR Tetris
...que ainda se encontra em construção !

---
DR Teris ==> Diogo Rigo Tetris :)
Este é o motivo do meu sumisso !
Em breve voltarei a postar meus tutorias de SDL.