TopFrame
Um slide show feito à mão...

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.


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.

frame1.zip O nosso topframe completo
 
online