Há
uns vinte anos atrás, quando os computadores
ainda eram vistos como gigantescas máquinas
processadoras de cálculos matemáticos,
o máximo dos máximos numa apresentação
multimídia eram os sistemas de slide show.
Dois projetores de slides, um gravador cassete e
um solver (ou dissolver como era mais conhecido)
- uma maquininha infernal que tratava de sincronizar
todo mundo para que a platéia pudesse apreciar
aquele show de tecnologia.
Hoje
é bem provável que poucas pessoas
saibam o que é um slide, talvez não
tenham visto ainda um gravador cassete e o solver,
bem o solver se não foi parar num museu então
deve estar encostado em algum armário empoeirado.
Mas
a técnica de apresentar idéias, conceitos,
conhecimentos enfim através do uso de imagens
estáticas ainda é muito usada. No
moderno mundo multimídia então nem
se fala. Existem inúmeros programas que fazem
o serviço braçal e uma outra quantidade
deles, custando os olhos da cara, pretendem ir um
pouco mais adiante, quase chegando nos portões
de entrada da animação propriamente
dita.
Que
fique claro: o slide show não é uma
animação tradicional, mas uma apresentação
de imagens de tal forma que o movimento entre elas
apenas sirva para realçar a idéia
a ser transmitida. Onde usar esse tipo de técnica
e como fazer?
Bem,
podemos usar o slide show em uma montanha de situações,
mas em nosso caso específico, aqui na TILT,
o lugar mais interessante é na abertura dos
nossos jogos. Ou ainda na apresentação
do enredo, no manual de funcionamento ou mesmo no
próprio jogo. Para tal, precisaremos responder
a segunda pergunta do parágrafo anterior:
"como fazer?".
Desde
que os PCs passaram a usar como padrão, nas
máquinas vendidas aos borbotões ao
distinto público, as placas Hi/True Color,
que o mundo não é mais o mesmo. No
tempo do CGA, do VGA
e ainda do SuperVGA já existiam
programas de slide show, mas para um programador
de fim de semana fazer uma reles rotina de apresentação
era quase impossível.
Hoje
com o Windows, o Delphi e um pouquinho de boa vontade,
o fuzuê está armado. E sem usar bitblt,
API, encapsulamento, herança, gororoba, frique-frique
e outros bichos tão ou mais complicados.
Podemos fazer um sistema de slide show em pouco
mais de uma hora de programação Delphi.
Coisa de deixar professor de C++ de cabelo em pé.
Vamos
aos fatos? Então, sigam-me por favor.
Primeiro
vamos dar nomes aos bois: leu na TILT
(aqui mesmo na seção Truques &
Técnicas) a matéria sobre ScanLines?
Não? Então perdeu o mais importante,
pois é tudo o que iremos usar aqui. Já
sabemos, de inúmeras matérias da TILT,
que não é conveniente plotarmos ou
imprimirmos na tela, nada diretamente. É
sempre preferível usar um buffer em memória
e só depois que todas as operações
forem feitas é que mandamos bala.
Aqui não será diferente. Vou apenas
estabelecer o formato da área de slide show
como 320 x 200 para facilitar a
compreensão da técnica, mas isso não
tem muita importância. É claro que
áreas maiores irão comprometer a performance
das rotinas e pode ser que seu 386 DX 40 não
dê conta de mostrar o efeito como planejado.
Se sentir que seu Pentium 4 de 2.5 Ghz está
tossindo um pouco para mostrar os efeitos, reduza
o tamanho das imagens. Lembre-se: estaremos trabalhando
com imagens True Color e portanto
gastando três vezes mais tudo - memória,
velocidade, HD, etc.
var Buf1,Buf2: TBitmap;
procedure TForm1.FormCreate(Sender: TObject); begin Buf1:= TBitmap.Create; Buf1.Width:= 320; Buf1.Height:= 200; Buf2:= TBitmap.Create; Buf2.Width:= 320; Buf2.Height:= 200; end;
procedure TForm1.FormDestroy(Sender: TObject); begin Buf1.Free; Buf2.Free; end; |
Podemos
adotar um sistema indireto de definição
de valores. Por exemplo, em vez de usar Buf1.Width:=
320, criamos uma constante Largura:
integer = 320 e Buf1.Width:=
Largura. Assim, se for preciso mudar o valor
das especificações da área
de apresentação, bastará mudar
a declaração da constante. Mas isso
fica como dever de casa para os leitores, afinal
precisa sobrar alguma coisa para vocês fazerem,
certo?
Nosso
sistema de apresentação será
baseado em dois Timers que funcionam
de forma distinta: um servirá para controlar
a execução de um roteiro e o outro
será o controle do delay, ou seja, o tempo
entre uma imagem e outra. Mais uma vez lembro aos
distintos leitores: slide show não é
animação quadro a quadro, mas sim
uma estória contada de forma semi-animada.
O timer
de delay é simplérrimo:
procedure TForm1.Timer2Timer(Sender: TObject); begin if Delay > 0 then dec(Delay) else begin Timer2.Enabled:= False; Timer1.Enabled:= True; end; end; |
Existe
um variável global (Delay: integer) que recebe
um valor. A cada "giro" do Timer2 essa variável
é decrementada até chegar a zero.
Neste instante o Timer2 é desligado e o Timer1
é religado.
O Timer1
executa um roteiro de apresentação,
que nada mais é do que um arquivo TXT que
contém o nome da tela, o efeito que se deseja
aplicar a ela, o tempo em segundos que ela deve
ser apresentada ao usuário e um parâmetro
de controle do efeito, se for o caso.
Aqui
em nosso exemplo, nem vamos nos dar ao trabalho
de criar uma arquivo TXT, mas simplesmente usaremos
um TMemo
com o seguinte roteiro:
top00002 direto 7 0 top00003 persiana 7 70 top00004 dissolve 7 70 top00005 fadeoff 7 20 top00003 fadeon 7 20 fim |
Apenas
para facilitar, defini quatro campos de 10 caracteres
cada um, sendo que os dois últimos caracteres
não são usados. A palavra "fim" no
final é apenas uma segurança a mais
de que o roteiro termina alí mesmo. Durante
a sua edição, geralmente acrescentamos
linhas em branco no final e isso pode causar um
rebuliço complicado em nosso sistema.
procedure TForm1.Timer1Timer(Sender: TObject); var Arquivo,Comando,Num: string; Param,Lp1,Lp2,Lp3,Lp4: integer; begin if (Roteiro.Lines[Linha] = 'fim') or (Roteiro.Lines.Count <= Linha) then begin Timer1.Enabled:= False; Timer2.Enabled:= False; exit;
end; Arquivo:= copy(Roteiro.Lines[Linha],1,8); Comando:= copy(Roteiro.Lines[Linha],11,8); Num:= copy(Roteiro.Lines[Linha],21,8); while pos(' ', Num) > 0 do Num[pos(' ', Num)]:= '0';
Delay:= StrToInt(Num); Num:= copy(Roteiro.Lines[Linha],31,8); while pos(' ', Num) > 0 do Num[pos(' ', Num)]:= '0'; Param:= StrToInt(Num); |
Pronto.
Pegamos o nome do arquivo, o efeito desejado e os
dois valores: tempo de apresentação
e parâmetro adicional.
//DIRETO: if Comando = 'direto ' then begin Buf1.LoadFromFile(Arquivo+'.bmp'); Canvas.Draw(10,10,Buf1); end; |
Este
é o efeito de apresentação
instantânea. Pimba, e a imagem está
na tela. É um recurso drástico, que
causa um tremendo impacto ao espectador. É
excelente quando se quer ressaltar uma imagem.
//PERSIANA: if Comando = 'persiana' then begin Buf2.LoadFromFile(Arquivo+'.bmp'); for Lp1:= 0 to 9 do begin for Lp2:= 0 to 19 do begin Orig:= Buf2.ScanLine[Lp1 + Lp2 * 10]; Dest:= Buf1.ScanLine[Lp1 + Lp2 * 10]; for Lp3:= 0 to 959 do Dest[Lp3]:= Orig[Lp3]; end; Canvas.Draw(10,10,Buf1); sleep(Param); end; end; |
Você
conhece esse efeito. É aquela persiana que
infesta as barreiras em campos de futebol, com propaganda
de bancos e planos de saúde. Mas é
um efeito supimpa de ser feito.
Considere
que dividimos a tela (200 linhas) em 20 blocos de
10 linhas cada um. Assim, num looping de 10 steps
plotamos uma a uma as linhas correspondentes de
cada bloco. A cada grupo plotado a imagem vai para
a área de apresentação. Note
que mantemos o Buf1 com a imagem original e carregamos
a imagem a ser mostrada no Buf2. É dele que
pegamos uma a uma as linhas para o Buf1.
//DISSOLVE: if Comando = 'dissolve' then begin Buf2.LoadFromFile(Arquivo+'.bmp'); for Lp4:= 0 to Param do begin for Lp1:= 0 to 199 do begin Orig:= Buf2.ScanLine[Lp1]; Dest:= Buf1.ScanLine[Lp1]; for Lp3:= 0 to 959 do begin Lp2:= Dest[Lp3] + Orig[Lp3]; if Lp2 > 0 then Lp2:= Lp2 div 2; Dest[Lp3]:= Lp2; end; end; Canvas.Draw(10,10,Buf1); sleep(Param); end; Canvas.Draw(10,10,Buf2); end; |
O dissolve
faz o seguinte: soma os dois componentes relativos
das telas (original e nova) e divide por dois. Essa
média passa a ser usada como imagem original
num próximo processamento. O parâmetro
adicional aqui é usados para definir quantas
vezes faremos essa operação e quanto
tempo esperaremos entre um e outro processamento.
A cada passagem, a tela anterior vai se aproximando
da tela final. Para garantir a perfeita impressão,
quando o ciclo termina é enviada para tela
a imagem definitiva, carregada no Buf2.
//FADEOFF: if Comando = 'fadeoff ' then begin Buf2.LoadFromFile(Arquivo+'.bmp'); for Lp4:= 0 to 255 do begin for Lp1:= 0 to 199 do begin Dest:= Buf1.ScanLine[Lp1]; for Lp3:= 0 to 959 do begin if Dest[Lp3] > 0 then dec(Dest[Lp3]); end; end; Canvas.Draw(10,10,Buf1); end; for Lp4:= 0 to 255 do begin for Lp1:= 0 to 199 do begin Dest:= Buf1.ScanLine[Lp1]; Orig:= Buf2.ScanLine[Lp1]; for Lp3:= 0 to 959 do begin if Dest[Lp3] <> Orig[Lp3] then inc(Dest[Lp3]); end; end; Canvas.Draw(10,10,Buf1); end; end; |
O fadeoff
apenas apaga (torna todos os pixels pretos) a tela
original e mostra a tela final clareando seus pontos
até a definição correta.
//FADEON: if Comando = 'fadeon ' then begin Buf2.LoadFromFile(Arquivo+'.bmp'); for Lp4:= 0 to 255 do begin for Lp1:= 0 to 199 do begin Dest:= Buf1.ScanLine[Lp1]; for Lp3:= 0 to 959 do begin if Dest[Lp3] < 255 then inc(Dest[Lp3]); end; end; Canvas.Draw(10,10,Buf1); end; for Lp4:= 0 to 255 do begin for Lp1:= 0 to 199 do begin Dest:= Buf1.ScanLine[Lp1]; Orig:= Buf2.ScanLine[Lp1]; for Lp3:= 0 to 959 do begin if Dest[Lp3] <> Orig[Lp3] then dec(Dest[Lp3]); end; end; Canvas.Draw(10,10,Buf1); end; end; |
O fadeon
faz o contrário - acende os pontos e depois
faz o inverso.
//Coloque o seu efeito a partir daqui
Timer1.Enabled:= False; Timer2.Enabled:= True; inc(Linha); end; |
E aqui
termina nosso Topframe
e seus cinco efeitos conhecidos de impressão
de imagens. Agora é reler para tirar as dúvidas,
baixar o pacote no computador abaixo e testar se
na prática a teoria é a mesma. Ou
não.
Depois
disso, criar um efeito especial supimpa e mandar
aqui para o club TILT.