Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Bild aus Spieldatei extrahieren (https://www.delphipraxis.net/133106-bild-aus-spieldatei-extrahieren.html)

WorstNightmare 25. Apr 2009 19:13


Bild aus Spieldatei extrahieren
 
Hallo,
diesmal habe ich ein Problem mit Bildern :)
Ich möchte ein Logo aus einer Spieldatei extrahieren. Der Inhalt muss zuerst mit ZLib dekomprimiert werden und dann werden die Pixel einzeln ausgelesen (ARGB). Es gibt ein C# Programm, welches das schon kann. Einen Teil davon wollte ich nun nach Delphi übersetzen und es scheint auch, ein bisschen zumindest, zu funktionieren, denn ich sehe einzelne Pixel einer fetten Schrift, allerdings keine farbigen Inhalte.

Code:
            MemoryStream stream = new MemoryStream();
            stream.Write(compressedBuffer, 2, compressedBuffer.Length - 2);
            byte[] buffer = new byte[decompressedSize];
            stream.Position = 0L;
            DeflateStream stream2 = new DeflateStream(stream, CompressionMode.Decompress);
            stream2.Read(buffer, 0, buffer.Length);
            stream2.Close();
            stream2.Dispose();
            stream.Close();
            stream.Dispose();

            Bitmap bitmap = new Bitmap(this.width, this.height);  // vorher ausgelesen
            int x = 0;
            int y = 0;

            for (int n = 0; n < decompressedSize; n += 2)
            {
                if (x == this.width)
                {
                    x = 0;
                    y++;
                    if (y == this.height)
                    {
                        break;
                    }
                }
                num9 = (buffer[n] & 0x1f) << 3; //b
                num9 |= num9 >> 5;
                num8 = ((buffer[n + 1] & 7) << 5) | ((buffer[n] & 0xe0) >> 3); // g
                num8 |= num8 >> 6;
                num7 = buffer[n + 1] & 0xf8;  // r
                num7 |= num7 >> 5;
                num6 = 0xff;   // a
                bitmap.SetPixel(x, y, Color.FromArgb(num6, num7, num8, num9));
                x++;
            }
Und nun meine Delphi Übersetzung:
Delphi-Quellcode:
    deSize := StreamDecompression(vBuf, vOutputStream);
    if deSize = -1 then
      Exit;

    vOutputStream.Position := 2;  // bei dem Stream.Write oben steht auch Offset 2

    bmp := TBitmap.Create;
    bmp.SetSize(FWidth, FHeight);

    n := 0;
    x := 0;
    y := 0;
    while n < deSize do
    begin
      if x = FWidth then
      begin
        x := 0;
        Inc(y);
        if y = FHeight then
          Break;
      end;

      vOutputStream.Read(b1, 1);
      vOutputStream.Read(b2, 1);

      b := (b1 and $1f) shl 3; //b
      b := b or (b shr 5);
      g := ((b2 and 7) shl 5) or ((b1 and $e0) shr 3); // g
      g := g or (g shr 6);
      r := b2 and $f8;  // r
      r := r or (r shr 5);
      a := $ff;   // a

      cc := a shl 24 + r shl 16 + g shl 8 + b;   // kann man das so machen?

      bmp.Canvas.Pixels[x, y] := cc;

      Inc(n, 2);
      Inc(x);
    end;
Dann habe ich noch eine halbwegs funktionierende Delphi Version :p (man kann alles erkennen, allerdings ist es teilweise etwas pixelig und die Farben sind etwas falsch)
Hier wird das Bitmap-Bild komplett manuell erzeugt
Delphi-Quellcode:
    FillChar(BF, SizeOf(TBitmapFileHeader) , 0);
    with BF do
    begin
      bfType := Ord('B') + Ord('M') * $100;  //2
      bfSize := 2 * vOutputStream.Size + SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfo);//4  ¾ã?¤å¥ó¤j¤p
      bfReserved1 := 0; //2
      bfReserved2 := 0; //2
      bfOffBits := SizeOf(TBitmapFileHeader) + SizeOf(TBitmapInfoHeader); //4 ?¤å¥ó?©l¨ì¦ì??Õu?©l¤§?ªº?Õu(bitmap data)¤§?ªº°¾²¾¶q
    end;
    vBMPStream.WriteBuffer(BF, SizeOf(TBitmapFileHeader));

    FillChar(BI, SizeOf(TBitmapInfo) , 0);
    with BI do
    begin
      BI.bmiHeader.biSize := SizeOf(TBitmapInfoHeader); //Bitmap Info Header)ªº?«×
      BI.bmiHeader.biWidth := FWidth;
      BI.bmiHeader.biHeight := FHeight;
      BI.bmiHeader.biPlanes := 1;
      BI.bmiHeader.biBitCount := 32;
      BI.bmiHeader.biCompression := BI_RGB;
    end;
    vBMPStream.WriteBuffer(BI, SizeOf(TBitmapInfo));

    for y := FHeight - 1 downto 0 do
    begin
      vOutputStream.Seek(2 * y * FWidth, 0);
      for i := 1 to FWidth do
      begin
        if vOutputStream.position > vOutputStream.size then
          pp := 0
        else
          vOutputStream.Read(pp, 2);

        a := (pp shr 12) and $0F;
        a := a * $11;
        r := (pp shr 8) and $0F;
        r := r * $11;
        g := (pp shr 4) and $0F;
        g := g * $11;
        b := (pp) and $0F;
        b := b * $11;
        cc := a shl 24 + r shl 16 + g shl 8 + b;      
        //cc := r shl 16 + g shl 8 + b;
        vBMPStream.Write(cc, 4); //vBMPStream§Y32¦ìBMP®æ¦¡ªº?¹³?Õu¬y
      end;
    end;
Kennst sich jemand mit solchen Dingen gut aus und sieht in dem ganzen einen Sinn? Ich hab keine Ahnung was diese ganze Bit-Rumschieberei bewirkt^^

WorstNightmare 26. Apr 2009 17:27

Re: Bild aus Spieldatei extrahieren
 
Vielleicht weiß jemand, was ich ändern muss, wenn er die Resultate sieht:

So ist es richtig (vom C# Programm):
http://www.abload.de/img/nxmdwz.png

So sieht es bei meiner Übersetzung aus:
http://www.abload.de/img/dcsharptf00.png

Und so macht es die halbrichtige Delphi Methode:
http://www.abload.de/img/delphikdok.png

Blup 29. Apr 2009 10:52

Re: Bild aus Spieldatei extrahieren
 
Vermutlich sind bei deiner Übersetzung die Variablen b1 und b2 als Byte deklariert.
Das führt dazu, daß bei den Bit-Verschiebungen ein großer Teil der Information einfach verschwindet.

var
b1, b2: Word;

{MSB des Word mit 0 initialisieren $0000}
b1 := 0;
b2 := 0;
{LSB in das Word laden $00xx}
vOutputStream.Read(b1, 1);
vOutputStream.Read(b2, 1);


Das Alpha-Byte gibt es bei TColor nicht (wird nur zur Kennzeichnung von Farben aus Paletten genutzt) und sollte deshalb Null sein.

cc := b shl 16 + g shl 8 + r; // $00bbggrr

oder

cc := RGB(r, g, b);

himitsu 29. Apr 2009 11:01

Re: Bild aus Spieldatei extrahieren
 
er kann r, b und g schon als Byte definieren, aber dann muß vor dem SHL die Größe angepaßt werden.
Delphi-Quellcode:
cc := Integer(b) shl 16 + Integer(g) shl 8 + r;
// bzw.
cc := TColor(b) shl 16 + TColor(g) shl 8 + r;

PS: für mich sieht es so aus, als wenn das zu lesende Bitmap als 16-Bit-Version gespeichert würde ... da könnte man es doch direkt, ohne pixelweise Umwandlung direkt in ein 16-Bit-Bitmap kopieren?

bzw. warum nicht einfach direkt als Bitmäp auslesen?
TImage/TBitmap können diese Bitmaps doch lesen und wenn ich mir das "selbst erzeugen" so anseh, sieht es ganz nach 'nem normalen Bitmap-Format aus :gruebel:

Blup 29. Apr 2009 12:12

Re: Bild aus Spieldatei extrahieren
 
Tatsächlich benötigt TBitmap zum Lesen auch einen entsprechenden Header in der Datei bzw. Stream
(dort sind z.B. Höhe, Breite, Pixelformat usw. definiert).
Da aber das Pixelformat pf16Bit und Breite/Höhe bekannt sind, kann man die Daten auch direkt in das Bitmap speichern.

Delphi-Quellcode:
  bmp.Pixelformat := pf16Bit;

for y := 0 to bmp.Height - 1 do
  AStream.Read(bmp.ScanLine[y]^, bmp.Width * 2);

WorstNightmare 29. Apr 2009 14:22

Re: Bild aus Spieldatei extrahieren
 
Danke für eure Tipps! Ich habe die ganzen Bytes in Words umgewandelt und benutze nun RGB(). Klappt wunderbar :thumb:

himitsu 29. Apr 2009 14:40

Re: Bild aus Spieldatei extrahieren
 
Zitat:

Zitat von WorstNightmare
Danke für eure Tipps! Ich habe die ganzen Bytes in Words umgewandelt und benutze nun RGB(). Klappt wunderbar :thumb:

dann sollten nun die Bytes auch ausreichen.

aber schneller ginge es halt, wenn du den Datenstrom direkt kopierst und nicht über das sehr langame .Pixels gehst.



PS zum Copyright: ist es überhaupt erlaubt Bilder aus dem Spiel rauszuholen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:50 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz