ScanLine
Turbine o acesso aos pixels da sua imagem

Se existe uma coisa que irrita os programadores de jogos e demais espécimes desta incrível fauna informática é a forma estúpida e bitolada com a qual os sistemas manipulam bits e bytes gráficos. Já vai longe o tempo em que se podia dar um Peek e um Poke, na área de vídeo, para reciprocamente plotar um ponto na tela do monitor.

No PC isso sempre foi um problemão e tanto, devido às rotinas inéptas que nos foram impostas (via DOS e INTs) notadamente quando se desejava usar modos gráficos. No Windows isso não mudou muito, apesar de estarmos trabalhando sob um ambiente gráfico (santa contradição, né não Batmam?).

Então, não era de se esperar que o Delphi nos presenteasse com uma solução inteligente, afinal ele foi feito para uso em programação de sistemas comerciais. Só mesmo esses doidos da TILT que usam Delphi para escrever jogos e acessar diretamente o vídeo. Ninguém mais faz isso.

Bem, se você se interessa pelo assunto mas está chegando agora no pedaço, então vamos rever alguns pontos básicos. Sem eles todo o resto não faz muito sentido.

No Windows dispomos de ferramentas (funções internas da API) que manipulam maravilhosamente áreas gráficas (bitmaps), transportando-as para lá e para cá, mais rápido que o Sedex. Fazem operações booleanas com essas áreas. Pintam e bordam. Mas o pobre coitado do pixel fica sempre a ver navios.

Experimente só rodar o seguinte programa:
procedure TForm1.SpeedButton1Click(Sender: TObject);
var
  X,Y: integer;
  Z: DWord;
begin
  Z:= Random(255)*65536 + Random(255)*256 + Random(255);
  for Y:= 0 to ClientHeight -1 do begin
    for
X:= 0 to ClientWidth -1 do Canvas.Pixels[X,Y]:= Z;
  end;
end;

X e Y são os valores que irão apontar pixel a pixel todos os pontos dentro da imagem; Z é uma cor que será definida aleatoriamente. Nossa procedure pode ser chamada de um CLS especial, colorido aleatoriamente. Quem faz o trabalho sujo (ou limpo, como preferirem) é a instrução Canvas.Pixels[X,Y]:= Z. É o antigo Poke na área de vídeo.

Mas tem um porém: isso é de uma lentidão exasperante. Tente rodar esse programinha em não menos que um Pentium 4 de 2.5 Ghz e você vai ver como a coisa empaca. Se pintar a tela à mão, fazemos mais rápido. O Windows não está preparado para lidar com uma coisa tão simples quanto um pixel.

A saída é fazer primeiro na memória e só depois mandar para o vídeo (ou mais precisamente para o canvas do formulário, que pode ser considerado como a nossa área particular de vídeo).

Para isso definimos um buffer que será a nossa imagem em memória (globalmente falando, é claro):
var
  Buffer: TBitmap;

Ao inicializar o programa, precisamos criar o buffer, carregá-lo com um padrão gráfico (é a forma mais rápida e fácil de definir a estrutura do bitmap, que no caso será padrão True Color):

procedure TForm1.FormCreate(Sender: TObject);
begin
    Buffer:= TBitmap.Create;
    Buffer.LoadFromFile('padrao2.bmp');
    Buffer.Width:= Form1.ClientWidth;
    Buffer.Height:= Form1.ClientHeight - 35;
end;

Não esquecer (não vamos deixar para depois) de destruir tudo o que foi feito, na saída:

procedure TForm1.FormDestroy(Sender: TObject);
begin
  Buffer.Free;
end;

Agora criamos um TSpeedButton2 e acrescentamos a seguinte programação:

procedure TForm1.SpeedButton2Click(Sender: TObject);
var
  X,Y: integer;
  Z: DWord;
begin
  Z:= Random(255)*65536 + Random(255)*256 + Random(255);
  for Y:= 0 to Buffer.Height -1 do begin
    for
X:= 0 to Buffer.Width -1 do
      Buffer.Canvas.Pixels[X,Y]:= Z;
  end;
  Canvas.Draw(0,0,Buffer);
end;

A diferença é que fizemos o "Poke" na memória e, no final, mandamos o buffer para o vídeo, todo de uma vez (Canvas.Draw(0,0,Buffer)). Muda alguma coisa, mas quase não se percebe. Ainda é um processo demorado e que não serve para animações e nem para usar em jogos mais elaborados.

Mas, salvos pelo gongo, existe uma função chamada ScanLine que atua não no pixel diretamente, mas numa linha de pixels. Mas antes é preciso criar uma variável pointer que apontará para a matriz da linha (variável global):
var
  Buffer: TBitmap;
  P: PByteArray;

Agora é usar o mesmo princípio da escrita em memória, que acabamos de ver:

procedure TForm1.SpeedButton3Click(Sender: TObject);
var
  X,Y: integer;
  R,G,B: byte;
begin
  R:= Random(255); G:= Random(255); B:= Random(255);
  for Y:= 0 to Buffer.height -1 do begin
    P:= Buffer.ScanLine[Y];
    for X:= 0 to Buffer.width -1 do begin
       P[X*3]:= R; P[X*3+1]:= G; P[X*3+2]:= B;
    end;
  end;

  Canvas.Draw(0,0,Buffer);
end;

Quase lá! Fica hiper rápido e não se nota o tempo entre soltar o botão e o processamento da imagem. Em computadores mais lentos (abaixo de 166 Mhz) pode até haver um pequeno "flicking" na imagem, mas é porque estamos usando cores em True Color e isso multiplica por três o tempo de processamento.

Veja como ficaria uma televisão que teima em ficar fora do ar:
procedure TForm1.Timer1Timer(Sender: TObject);
var
  X,Y: integer;
begin
  if
Vid1 = 1 then begin
    for
Y:= 0 to Buffer.height -1 do begin
      P:= Buffer.ScanLine[Y];
      for X:= 0 to (Buffer.width *3)-1 do P[X]:= Random(255);
    end;
    Canvas.Draw(0,0,Buffer);
  end;
end;

O processamento está dentro de um TTimer, ajustado para um intervalo igual a 10. Basta definir uma variável byte (Vid1 = 0) e um botão para ligar e desligar a TV. Só fica faltando mesmo o som.

Podemos melhora ainda mais essas procedures. O truque é definir a área de imagem com o padrão 8 bits, para as cores. Assim, teríamos uma palette que poderia servir a uma grande quantidade de imagens e poderíamos tratar nossas figuras como se cada pixel fosse um único byte. Coisa fácil e rápida de ser feita.

Mas isso eu deixo para vocês (ou para uma outra oportunidade).


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.

tv1.zip A televisão fora do ar...
 
online