Einzelnen Beitrag anzeigen

Benutzerbild von olee
olee

Registriert seit: 16. Feb 2008
Ort: Boppard
540 Beiträge
 
Turbo Delphi für Win32
 
#9

AW: Schnelle Bildschirmübertragung per Netzwerk

  Alt 16. Okt 2012, 20:07
Ich habe erst vor kurzem eine sehr einfache aber effiziente Methode der Übertragung von Bildschirminhalt geschrieben.
Das Prinzip war folgendermaßen:

-> Mit Scanline letzten und nächten Frame vom Bildschirm pixelweise durchgehen.
---> Wenn ein (außreichend großer?) Unterschied von beiden Pixeln festgestellt wird, werden in den Stream die Pixelnummer (zeile*width+x) und ein Platzhalter für die Anzahl an Pixeln geschrieben
---> Solange die folgenden Pixel sich von dem Original unterscheiden (+ lookahead von 2 Pixeln) wird jeder weitere Pixel in den Stream geschrieben.
---> Wenn 3 aufeinanderfolgende Pixel wieder gleich sind, wird mit dem schreiben aufgehört.
---> Wenn der Schreibvorgang beendet wird, wird noch die Anzahl geschriebener Pixel an den Platzhalter geschrieben
---> Bei durchgehen aller Pixel werden dabei Zeilenenden ignoriert.
-> Der Finale Stream wird noch mit ZLib gepackt.

-> Das Wiederherstellen des Bildes beim Zielrechner geschieht analog.

Hier mal die beiden Funktionen dafür:
Delphi-Quellcode:
procedure compressStream(input, output: TStream);
var
  xx: TCompressionStream;
begin
  xx := TCompressionStream.Create(clmax, output);
  try
    input.Position := 0;
    xx.CopyFrom(input, input.Size);
  finally
    xx.Free;
    output.position := 0;
  end;
end;

procedure TForm1.WriteDiffPkg(last, next: TBitmap; zipPkg: TMemoryStream);
var
  w, h : Integer;
  ix, iy : Integer;
  pixpos : Cardinal;
  cnt : Cardinal;

  addr : Int64;
  pl, pn : PRGBTriple;
  diff : Boolean;

  pkg : TMemoryStream;
type
  PLongWord = ^LongWord;
begin
  pkg := TMemoryStream.Create;
  try
    w := last.Width;
    h := last.Height;

    pixpos := 0;
    diff := false;
    for iy := 0 to h - 1 do
    begin
      pl := last.ScanLine[iy];
      pn := next.ScanLine[iy];
      for ix := 0 to w - 1 do
      begin
        If ((PLongWord(pl)^ and $F0F0F0) = (PLongWord(pn)^ and $F0F0F0)) then
        begin
          If diff then
          begin
            inc(pl); inc(pn);
            If ((PLongWord(pl)^ and $F0F0F0) = (PLongWord(pn)^ and $F0F0F0)) then
            begin
              inc(pl); inc(pn);
              If ((PLongWord(pl)^ and $F0F0F0) = (PLongWord(pn)^ and $F0F0F0)) then
              begin
                PCardinal(Integer(Pkg.Memory) + addr)^ := cnt;
                diff := false;
              end;
              dec(pl); dec(pn);
            end;
            dec(pl); dec(pn);
            If diff then
            begin
              Pkg.Write(pn^, 3);
// pl^.rgbtRed := 0;
// pl^.rgbtGreen := 0;
// pl^.rgbtBlue := 0;
              inc(cnt);
            end;
          end;
        end
        else
        begin
          If not diff then
          begin
            cnt := 0;
            addr := Pkg.Size;
            Pkg.WriteCardinal(0);
            Pkg.WriteCardinal(pixpos);
            diff := true;
          end;
          Pkg.Write(pn^, 3);
// pl^.rgbtRed := 0;
// pl^.rgbtGreen := 0;
// pl^.rgbtBlue := 0;
          inc(cnt);
        end;
        inc(pixpos);
        inc(pl);
        inc(pn);
      end;
    end;
    Pkg.WriteCardinal(0);
  finally
    zipPkg.Clear;
    pkg.Position := 0;
    compressStream(pkg, zipPkg);
    pkg.Free;
  end;
end;

procedure TForm1.ReadDiffPkg(last: TBitmap; zipPkg: TStream);
var
  w, h : Integer;
  ix, iy : Integer;
  pixpos : Cardinal;
  cnt : Cardinal;

  addr : Cardinal;
  ssize : Int64;
  pix : PRGBTriple;

  pkg : TDecompressionStream;
type
  PLongWord = ^LongWord;
begin
  zipPkg.Position := 0;
  pkg := TDecompressionStream.Create(zipPkg);
  try
    w := last.Width;
    h := last.Height;

    ix := 0;
    iy := 0;
    pixpos := 0;
    pix := last.ScanLine[iy];
    //while Pkg.Position < Pkg.Size do
    while true do
    begin
      cnt := Pkg.ReadCardinal;
      If cnt = 0 then break;
      addr := Pkg.ReadCardinal;

      // Fast-forward to address
      ix := addr mod w;
      iy := addr div w;
      pix := last.ScanLine[iy];
      inc(pix, ix);

      // Write data
      while cnt > 0 do
        If (ix < w) then
        begin
          Pkg.Read(pix^, 3);
          inc(ix);
          inc(pixpos);

          dec(cnt);
          inc(pix);
        end
        else
        begin
          inc(iy);
          pix := last.ScanLine[iy];
          ix := 0;
        end;
    end;
  finally
    pkg.Free;
  end;
end;
Bei Interesse kann ich auch noch eine Variation der Funktion posten, welche per Flag nur jede gerade/ungerade Zeile verarbeitet, um schnellere Updates zu ermöglichen.
Diese Technik hat sich im Praxistest als unglaublich schnell für die Übertragung von Textinhalten erwiesen.
Man kann bestimmt das ganze durch Bittiefenreduzierung noch optimieren...
Björn Zeutzheim
Codename: Performancepumpe
  Mit Zitat antworten Zitat