Efeitos em bitmaps
Matheus
Degiovani
Efeitos
especiais sempre incrementam o visual de qualquer
aplicação e são elementos necessários
em quase todos os tipos de jogos. No entantoé
comum que os programadores reescrevam uma rotina
para espelhar uma imagem ou realizar uma operação
de alpha blending sempre que preciso.
Pensando
em facilitar a sua vida, a reunimos dez efeitos
que podem ser usados em bitmaps de 24
bits com exatamente uma linha de
código, agilizando o desenvolvimento de qualquer
jogo. Clique aqui
e baixe um programinha exemplo, para acompanhar
os resultados de cada aplicação de
filtro, descrita a seguir.
Os
efeitos:
Para
utilizar as rotinas, basta declarar a unit
"tiltefeitos" na seção
uses do formulário, lembrando-se de colocar
o arquivo tiltefeitos.pas
no diretório do projeto ou em um que esteja
no caminho de procura do Delphi. O link para baixar
o fonte do tiltefeitos.pas
está no final desta matéria.
Todas
as funções reunidas trabalham pixel-a-pixel,
em bitmaps de 24 bits. Para executar
as operações de leitura e escrita,
é utilizada a função scanline,
que retorna um ponteiro para o endereço de
memória de uma determinada linha da imagem.
Para maiores informações sobre essa
função, leia o artigo "Scanline"
aqui mesmo no club TILT, no menu
de matérias, da seção Programação.
Os
seguintes efeitos foram criados (os nomes são
os usados nos próprios procedimentos):
fxFiltro(bmp:
TBitmap; cor: TColor): Aplica um "filtro"
de uma determinada cor na imagem passada como parâmetro.
fxBrilho(bmp:
TBitmap; valor: byte): Aumenta o brilho
de uma imagem, de acordo com o valor especificado
de 0 a 255.
fxEspelharHorz(bmp:
TBitmap): Espelha a imagem horizontalmente,
isto é, o lado direito passa a ser o esquerdo
e vice-versa.
fxEspelharVert(bmp:
TBitmap): Espelha a imagem verticalmente,
isto é, o lado superior passa a ser o inferior
e vice-versa.
fxScanline(bmp:
TBitmap): Aplica um efeito de "scanline"
sobre a imagem (linhas horizontais intercaladas).
fxBlur(bmp:
TBitmap): Aplica um efeito de blur na imagem
(tira a imagem de foco).
fxEmboss(Bmp:
TBitmap): Executa um efeito do tipo "emboss"
na imagem (destaca as transições de
cores)
fxSharpen(bmp:
TBitmap): Torna a imagem mais áspera
(nítida).
fxCopiarComAlfa(src,
dst: TBitmap; x, y: integer; alfa: byte):
Copia uma imagem (src) para outra (dst) porém
executando um efeito de alpha-blending de acordo
com o valor (alpha) especificado (ou seja, o valor
de opacidade que pode variar de 0 - transparente
- até 255 - opaco).
fxGrayscale(bmp:
TBitmap): Transforma uma imagem em tons
de cinza (não muda o número de bits
da imagem).
fxConvolution(bmp:
TBitmap; matrix: TConvoMatrix; divisor, bias: single):
Não é exatamente um efeito em si,
mas um procedimento que aplica um filtro de convolução
(Convolution Filter) na imagem (leia sobre esse
tipo de filtro abaixo).
O
programa de demonstração executa todos
esses efeitos em uma determinada imagem que pode
ser carregada do disco, no formato bmp (bitmap).
A função que aplica o filtro na imagem,
depois que ela foi carregada, é bem simples:
ela requer uma imagem fonte (pode ser um TImage
ou TBitmap) e executa o efeito
de acordo com a solicitação do usuário.
Por exemplo:
fxFiltro(ImagemFonte.Picture.Bitmap, clCorDoFiltro);
|
Filtro
e Brilho:
As
funções fxFiltro
e fxBrilho são diretas:
dada uma cor de filtro, um índice é
calculado para a cor e multiplicado pela cor de
cada pixel da imagem. O valor encontrado é
colocado entre os valores 0 e 255 e recolocados
na imagem.
procedure fxFiltro(bmp: TBitmap; cor: TColor); var r,g,b: byte; ir,ig,ib: single; x,y: integer; nr,ng,nb: single; c: TColor; begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; colortorgb(cor, b, g, r); ir:= r / 255; ig:= g / 255; ib:= b / 255; for x:= 0 to bmp.width -1 do for y:= 0 to bmp.height -1 do begin c:= getScanline(bmp, x, y); colortorgb(c, b, g, r); nr:= r + (r * ir); if nr > 255 then nr:= 255; ng:= g + (g * ig); if ng > 255 then ng:= 255; nb:= b + (b * ib); if nb > 255 then nb:= 255; c:= RGBToColor(trunc(nb), trunc(ng), trunc(nr)); setScanline(bmp, x, y, c); end; end;
|
A função
de brilho é simplesmente um filtro branco
aplicado à imagem.
procedure fxBrilho(bmp: TBitmap; valor: byte); begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; fxFiltro(bmp, RGBToColor(valor, valor, valor)); end;
|
Espelhos
e Scanline:
As
funções fxEspelharHorz
e fxEspelharVert são ainda
mais simples que as anteriores: elas apenas copiam
os dados de um pixel da imagem para outro pixel
na extremidade oposta. No primeiro efeito isso é
feito horizontalmente e no segundo verticalmente.
procedure fxEspelharHorz(bmp: TBitmap); var x,y: integer; aux: TColor; begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; for y:= 0 to bmp.Height -1 do for x:= 0 to (bmp.Width -1) div 2 do begin aux:= getScanline(bmp, bmp.width -1 -x, y); setScanline(bmp,bmp.width-1-x,y,getScanline(bmp,x,y)); setScanline(bmp, x, y, aux); end;
end;
procedure fxEspelharVert(bmp: TBitmap); var aux: TColor; x,y: integer; begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; for x:= 0 to bmp.width -1 do for y:= 0 to (bmp.height -1) div 2 do begin aux:= getScanline(bmp, x, y); setScanline(bmp,x,y,getScanline(bmp,x,bmp.height-1-y)); setScanline(bmp, x, bmp.height -1 -y, aux); end; end; |
O efeito
fxScanline percorre a imagem verticalmente
e, à cada duas linhas, preenche uma com a
cor preto.
procedure fxScanline(bmp: TBitmap); var x,y: integer; begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; y:= 0; while (y < bmp.height) do begin for x:= 0 to bmp.width -1 do setScanline(bmp, x, y, clBlack); y:= y + 2; end; end; |
Filtros
de Convolução:
Os
efeitos de convolution filters são um dos
mais famosos e usados no mundo do processamento
gráfico. Eles consistem no uso de uma matriz
chamada kernel (com tamanho que
pode ser especificado) que indica qual o "peso"
das cores que estão ao redor de um pixel
sendo lido. Esses resultados são somados
junto com um valor de "bias" e divididos
por um número divisor e então retornados
à imagem.
Ou
seja, o algoritmo para esse tipo de efeito percorre
cada pixel da imagem somando os valores dos pixels
adjacentes (multiplicados antes pela matrix kernel)
e divindo o valor resultante por um divisor.
Diferentes
kernels resultam em diferentes imagens, e por esse
motivo as funções fxBlur,
fxSharpen e fxEmboss
se traduzem em chamadas para a aplicação
de um filtro de convolução (fxConvolution)
apenas mudando-se os valores da matriz.
procedure fxBlur(bmp: TBitmap); var mat: TConvoIndexMatrix; begin mat[-1,-1]:= 1; mat[ 0,-1]:= 1; mat[+1,-1]:= 1; mat[-1, 0]:= 1; mat[ 0, 0]:= 1; mat[+1, 0]:= 1; mat[-1,+1]:= 1; mat[ 0,+1]:= 1; mat[+1,+1]:= 1; fxConvolution(bmp, getConvoMatrix(mat), 9, 0 ); end;
procedure fxSharpen(bmp: TBitmap); var mat: TConvoIndexMatrix; begin mat[-1,-1]:= -1; mat[ 0,-1]:= 0; mat[+1,-1]:= 0; mat[-1, 0]:= 0; mat[ 0, 0]:= 3; mat[+1, 0]:= 0; mat[-1,+1]:= 0; mat[ 0,+1]:= 0; mat[+1,+1]:= -1; fxConvolution(bmp, getConvoMatrix(mat), 1, 0 ); end;
procedure fxEmboss(Bmp: TBitmap); var mat: TConvoIndexMatrix; begin mat[-1,-1]:= -1; mat[ 0,-1]:= -1; mat[+1,-1]:= 0; mat[-1, 0]:= -1; mat[ 0, 0]:= 0; mat[+1, 0]:= +1; mat[-1,+1]:= 0; mat[ 0,+1]:= +1; mat[+1,+1]:= +1; fxConvolution(bmp, getConvoMatrix(mat), 1, 128 ); end;
|
Alpha
Blending e Grayscale:
A operação
de cópia com alpha blending resulta numa
imagem de destino sobreposta pela imagem fonte porém
com a sua opacidade reduzida (ou seja, consegue-se
ver partes da imagem de destino "embaixo"
da imagem fonte), ao contrário da cópia
normal que simplesmente sobrepõe os dados.
procedure fxCopiarComAlfa(src, dst: TBitmap; x, y: integer; alfa: byte); var cx,cy,ax,ay: integer; sr,sg,sb,dr,dg,db: byte; begin if src.PixelFormat <> pf24bit then src.PixelFormat:= pf24bit; if dst.PixelFormat <> pf24bit then dst.PixelFormat:= pf24bit; if x >= dst.Width then exit; if y >= dst.height then exit; ay:= 0; for cy:= y to min(dst.height-1,y+src.height-1) do begin ax:= 0; for cx:= x to min(dst.width-1,x+src.width -1) do begin colortorgb(getScanline(src, ax, ay), sr, sg, sb); colortorgb(getScanline(dst, cx, cy), dr, dg, db); dr:= dr + ((sr - dr) * alfa) shr 8; dg:= dg + ((sg - dg) * alfa) shr 8; db:= db + ((sb - db) * alfa) shr 8; setScanline(dst, cx, cy, RGBToColor(dr, dg, db)); ax:= ax + 1; end; ay:= ay + 1; end; end; |
Esse
efeito se traduz como uma subtração,
uma multiplicação e uma divisão
dos valores das cores da fonte e do destino, que
resulta no efeito desejado.
procedure fxGrayscale(bmp: TBitmap); var x,y: integer; r,g,b,nr,ng,nb: byte; begin if bmp.PixelFormat <> pf24bit then bmp.PixelFormat:= pf24bit; for x:= 0 to bmp.width -1 do for y:= 0 to bmp.height -1 do begin colortorgb(getScanline(bmp, x, y), r, g, b); nr:= trunc(r * 0.2125 + g * 0.7154 + b * 0.0721); ng:= nr; nb:= nr; setScanline(bmp, x, y, rgbToColor(nr, ng, nb)); end; end;
|
A transformação
para preto e branco é semelhante, porém
ela apenas executa uma multiplicação
entre os pixels da imagem e valores de ponderação
para os elementos vermelho (R), verde (G) e azul
(B) da imagem que retornam a imagem em escala de
cinzas.
Concluindo:
Os
efeitos expostos nesse artigo podem servir de inspiração
para diversas outras funções interessanes,
especialmente os filtros de convolução
que podem ser facilmente
criados. Portanto, mãos à obra!
Alguns kernels para os filtros de convolução.
http://www.sgi.com/software/opengl/advanced97/notes/node152.html
|
|
|
Download...
Clique no link para fazer
o download dos arquivos. Se sua assinatura do club TILT
está para vencer, clique
aqui e saiba como renová-la.
|
Fonte
completo do programa exemplo |
|
Fonte
das funções |
|
|
|
|