Tutorial de SDL – Parte 40 – Manipulação de texturas – Kleber's Blog
Para criar efeitos gráficos frequentemente é necessário acesso aos pixels. Nesse artigo, iremos alterar os pixels de uma imagem para clarear o fundo.
Aqui iremos adicionar nova funcionalidade à classe da textura. Temos funções para bloquear/desbloquear a textura pois para podermos acessar os pixels da textura temos que bloqueá-la e uma vez que tivermos terminados de lidar com os pixels temos que desbloqueá-la.
Temos uma função para obter os pixels brutos e uma função para obter o pitch. O pitch é basicamente a largura da texteura na memória. Em alguns equipamentos mais antigos e portáteis, existem limitações do tamanho da textura que você pode ter. Se você criar uma textura com uma lardura de 100 pixels, pode ser que ela seja alterada para 128 pixels (a potência de 2 mais próxima). Usando o pitch, saberemos como a imagem é armazenada na memória.
Em termos de atributos, temos um ponteiro para os pixels após o bloqueio da textura e o pitch.
SDL_Surface* formattedSurface = SDL_ConvertSurface( loadedSurface, SDL_GetWindowSurface( gWindow )->format, NULL );
printf( "Unable to convert loaded surface to display format! SDL Error: %s\n", SDL_GetError() );
newTexture = SDL_CreateTexture( gRenderer, SDL_GetWindowPixelFormat( gWindow ), SDL_TEXTUREACCESS_STREAMING, formattedSurface->w, formattedSurface->h );
Para que sejamos capazes de editar a textura, temos que carregar nossa textura de forma diferente. Quando criamos texturas a partir de superfícies em artigo anteriores, elas tinham o valor de SDL_TextureAccess padrão SDL_TEXTUREACCESS_STATIC, o que significava que não podíamos altera-la após ter sido criada. Para podermos ser capazes de editar os pixels da textura temos que criar a textura como SDL_TEXTUREACCESS_STREAMING. Primeiro temos que carregar a imagem como uma superfície como fizemos anteriormente. Então temos que converter a superfície para o mesmo formato de pixel da janela. Por fim, criamos uma textura em branco com SDL_Createtexture.
SDL_LockTexture( newTexture, NULL, &mPixels, &mPitch );
Depois que a textura é criada, temos que manualmente copias os pixels da superfície para a textura. Para capturas os pixels da textura usamos SDL_LockTexture. O primeiro argumento é a textura que queremos capturar seus pixels. O segundo argumento é a região de onde queremos capturas os pixels, e como iremos capturas os pixels de toda a textura configuramos esses argumento como NULL. O terceira argumento é o ponteiro que será usado como endereço dos pixels, e o último argumento será configurada com o pitch da textura.
Depois que tivermos os pixels da textura, copiamos os pixels da superfície para a textura usando memcpy. O primeiro argumento é o destino, o segundo é a origem, e o terceiro argumento é o número de bytes que iremos copiar. Felizmente, o pitch do SDL nos fornece o número de bytes por linha de pixels, de forma que precisamos apenas multiplicar ele pela altura da superfície para copiar todos os pixels da image. Quando tivermos copiados os pixels da superfície para a textura, desbloqueamos a textura para atualiza-la com os novos pixels usando SDL_UnlickTexture. Depois que a textura é desbloqueada o ponteiro de pixels é invalidado configurando ele como NULL.
Com os pixels da superfície copiados para a textura, nos livramos das superfícies antigas e retornamos true se a textura tiver sido carregada com sucesso.
bool LTexture::lockTexture()
//Texture is already locked
printf( "Texture is already locked!\n" );
//Lock texture
if( SDL_LockTexture( mTexture, NULL, &mPixels, &mPitch ) != 0 )
printf( "Unable to lock texture! %s\n", SDL_GetError() );
bool LTexture::unlockTexture()
//Texture is not locked
if( mPixels == NULL )
printf( "Texture is not locked!\n" );
SDL_UnlockTexture( mTexture );
mPitch = 0;
Aqui temos nossas função de bloqueio e desbloqueio da textura após o carregamento da imagem.
void* LTexture::getPixels()
return mPixels;
int LTexture::getPitch()
Finalmente, temos os métodos de acesso para os pixels e para o pitch enquanto a textura estiver bloqueada. Agora que criamos uma textura que pode ser editável e bloqueada/desbloqueada, chegou a hora de executar algum processamento nos pixels da textura.
if( !gFooTexture.loadFromFile( "40_texture_manipulation/foo.png" ) )
printf( "Failed to load corner texture!\n" );
if( !gFooTexture.lockTexture() )
printf( "Unable to lock Foo' texture!\n" );
Na nossa função de carregamento de mídia, depois que carregamos a textura, bloqueamos ela de forma que possamos alterar os pixels.
//Manual color key
Uint32* pixels = (Uint32*)gFooTexture.getPixels();
int pixelCount = ( gFooTexture.getPitch() / 4 ) * gFooTexture.getHeight();
Depois que a textura é bloqueada, iremos percorrer os pixels e tornar todos os pixels do fundo transparentes. O que estamos fazendo é essencialmente manualmente codificando as cores da imagem.
Primeiro precisamos capturas todos os pixels. Nosso método de acesso aos pixels retorna um ponteiro void e queremos pixels de 32 bits, assim sobrecarregamos o tipo para um inteiro sem sinal de 32 bits (unsigned integer).
Em seguida, queremos obter o número de pixels. Temos o pitch que é a largura em bytes. Precisamos da largura em pixels e como temos 4 bytes por pixel. tudo que precisamos fazer é dividir por 4 para obtermos o pitch em pixels. Então multiplicamos o pitch pela altura para o obter o número total de pixels.
Uint32 colorKey = SDL_MapRGB( SDL_GetWindowSurface( gWindow )->format, 0, 0xFF, 0xFF );
Uint32 transparent = SDL_MapRGBA( SDL_GetWindowSurface( gWindow )->format, 0xFF, 0xFF, 0xFF, 0x00 );
gFooTexture.unlockTexture();
O que iremos fazer é encontrar todos os pixels da codificação de cores e substitui-los por pixels transparentes. Primeiro mapeamos o código de cores e a cor transparente. Em seguida percorremos todos os pixels e verificamos se o pixel bate com a codificação das cores. Se bater, damos a ele o valor de um pixel transparente.