TGA Bilder laden: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
(Landkarte und Kompass - oder besser GPS :D)
K (Der Ausdruck ''<pascal>(.*?)</pascal>'' wurde ersetzt mit ''<source lang="pascal">$1</source>''.)
 
(12 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 1: Zeile 1:
 +
= TGA Bilder laden =
 +
 
== TGA Dateien - Warum? ==
 
== TGA Dateien - Warum? ==
  
In diesem Tutorial (mein erstes) geht es um das manuelle Laden von Truevision
+
In diesem Artikel geht es um das manuelle Laden von Truevision
 
Bilddateien mit der allseits bekannten Endung ".tga".
 
Bilddateien mit der allseits bekannten Endung ".tga".
  
 
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller
 
Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller
 
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen
 
Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen
auch schon in einer von neueren OpenGL-Implementierungen direkt benutzbaren
+
auch schon in einer von "neueren" OpenGL-Implementierungen (konkret: 1.2) direkt benutzbaren
 
Ordnung vor.
 
Ordnung vor.
  
Zeile 39: Zeile 41:
 
  Image Pixel Size    - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal
 
  Image Pixel Size    - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal
 
                         vorhanden ist.
 
                         vorhanden ist.
  Identification Field - die eigentlichen Pixeldaten.
+
  Image Data Field     - die eigentlichen Pixeldaten
  
 
Damit haben wir alles benötigte zusammen und können diese Informationen schon
 
Damit haben wir alles benötigte zusammen und können diese Informationen schon
Zeile 45: Zeile 47:
 
in unserem Dokument von wotsit):
 
in unserem Dokument von wotsit):
  
<pascal>
+
<source lang="pascal">
  type
+
type
    TTGAFile = packed record
+
  TTGAFile = record
      iType: byte; // should be 2
+
    iType: byte; // should be 2
      w, h: word; // Width, Height
+
    w, h: word; // Width, Height
        bpp: byte; // Byte per Pixel
+
      bpp: byte; // Byte per Pixel
      data: ^byte; // Pixels, dynamic length
+
    data: ^byte; // Pixels, dynamic length
    end;
+
  end;
</pascal>
+
</source>
  
 
== Die Datei laden ==
 
== Die Datei laden ==
Zeile 68: Zeile 70:
 
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.
 
sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.
  
<pascal>
+
<source lang="pascal">
  function LoadTGA(const filename: string): TTGAFile;
+
function LoadTGA(const filename: string): TTGAFile;
  var f: file; bytes: longword;
+
var f: file; bytes: longword;
  begin
+
begin
    assign(f, filename); reset(f, 1);
+
  assign(f, filename);
 +
  reset(f, 1);
  
    // type
+
  // type
    seek(f, 2); blockread(f, result.iType, 1);
+
  seek(f, 2);
 +
  blockread(f, result.iType, 1);
  
    // w, h, bpp
+
  // w, h, bpp
    seek(f, 12); blockread(f, result.w, 5);
+
  seek(f, 12);
    result.bpp := result.bpp div 8;
+
  blockread(f, result.w, 5);
 +
  result.bpp := result.bpp div 8;
  
    // data
+
  // data
    bytes := result.w * result.h * result.bpp;
+
  bytes := result.w * result.h * result.bpp;
    getmem(result.data, bytes);
+
  getmem(result.data, bytes);
    seek(f, 18); blockread(f, result.data^, bytes);
+
  seek(f, 18);
 +
  blockread(f, result.data^, bytes);
  
    close(f);
+
  close(f);
  end;
+
end;
</pascal>
+
</source>
  
 
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis
 
Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis
Zeile 101: Zeile 107:
 
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen
 
hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen
 
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit
 
den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit
<pascal>
+
<source lang="pascal">
  FreeMem(TGARecord.data);
+
FreeMem(TGARecord.data);
</pascal>
+
</source>
 
wieder freizugeben!
 
wieder freizugeben!
  
<pascal>
+
<source lang="pascal">
  procedure TGATexture(const filename: string; var TexID: longword);
+
procedure TGATexture(const filename: string; var TexID: longword);
  var
+
var
    tex: TTGAFile;
+
  tex: TTGAFile;
    glFormat: longword;
+
  glFormat: longword;
 +
begin
 +
  tex := LoadTGA(filename);
 +
  if tex.iType = 2 then
 
   begin
 
   begin
     tex := LoadTGA(filename);
+
     glGenTextures(1, @TexID);
    if tex.iType = 2 then
+
    glBindTexture(GL_TEXTURE_2D, TexID);
    begin
 
      glGenTextures(1, @TexID);
 
      glBindTexture(GL_TEXTURE_2D, TexID);
 
  
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
      glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  
      if tex.bpp = 3 then glFormat := GL_BGR
+
    if tex.bpp = 3 then glFormat := GL_BGR
        else glFormat := GL_BGRA;
+
      else glFormat := GL_BGRA;
  
      glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,
+
    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,
        0, glFormat, GL_UNSIGNED_BYTE, tex.data);
+
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);
  
      FreeMem(tex.data);
+
    FreeMem(tex.data);
    end;
 
 
   end;
 
   end;
</pascal>
+
end;
 +
</source>
  
Ich hoffe dieses Tutorial war hilfreich (Ich will Feedback!) und wünsche euch
+
Ich hoffe dieser Artikel war hilfreich (Ich will Feedback!) und wünsche euch
 
viel Spaß und Erfolg bei euren zukünftigen Projekten.
 
viel Spaß und Erfolg bei euren zukünftigen Projekten.
  
 
Gruß Waran.
 
Gruß Waran.
 +
 +
[[Kategorie:Technik oder Algorithmus]]

Aktuelle Version vom 10. März 2009, 19:10 Uhr

TGA Bilder laden

TGA Dateien - Warum?

In diesem Artikel geht es um das manuelle Laden von Truevision Bilddateien mit der allseits bekannten Endung ".tga".

Dieses Dateiformat ist für den Anfang aus verschiedenen Gründen auf voller Linie empfehlenswert. Es ist sehr einfach aufgebaut und die Pixeldaten liegen auch schon in einer von "neueren" OpenGL-Implementierungen (konkret: 1.2) direkt benutzbaren Ordnung vor.

Komprimierte TGA-Dateien lassen wir außen vor, da sie leider einige Nachteile mit sich bringen: Es ist schwieriger die Datei einzulesen, das Dekodieren benötigt wertvolle Rechenzeit und die RLE-Kompression, die in den TGA-Dateien zum Einsatz kommt, ist vergleichsweise schlecht.

Landkarte und Kompass - oder besser GPS?

Zuerst stellen wir uns die Frage, wie eine TGA-Datei intern aufgebaut ist. Da es sich um eine Binärdatei handelt, lässt sich die Bedeutung der Daten nicht erraten - wir sind also auf fremde Dokumentation angewiesen. Eine ganze Sammlung solcher "Datei-Spickzettel" findet man auf der Internetseite http://www.wotsit.org/.

Nachdem wir uns das entsprechende Dokument von dort besorgt und kritisch überflogen haben, fällt wohl erstmal ins Auge, dass es verschiedene Typen von TGA-Dateien zu geben scheint.

Wir beschäftigen uns hier nur mit Typ 2 (24/32-Bit TGA ohne Kompression), da es das gebräuchlichste Format ist.

Definition eines Datentyps

Welche Daten aus der TGA-Datei sind nun wirklich für unsere Zwecke interessant?

 Image Type Code     - zum überprüfen, ob die Datei das richtige Format hat.
  Width of Image
 Height of Image
Image Pixel Size     - anhand dieser können wir u.a. ermitteln, ob ein Alphakanal
                       vorhanden ist.
Image Data Field     - die eigentlichen Pixeldaten

Damit haben wir alles benötigte zusammen und können diese Informationen schon mal in einen Verbundtyp packen (Die Datentypen entnehmen wir der "Length"-Spalte in unserem Dokument von wotsit):

type
  TTGAFile = record
    iType: byte;  // should be 2
     w, h: word;  // Width, Height
      bpp: byte;  // Byte per Pixel
     data: ^byte; // Pixels, dynamic length
  end;

Die Datei laden

Die Schritte, die nun erforderlich sind, um diese Struktur mit Leben zu Füllen sind denkbar einfach. Zuerst wird die Datei binär geöffnet; der Dateizeiger mit Seek positioniert (siehe "Offset"-Spalte in unserem Dokument) und mit Blockread gelesen. Die Felder w, h und bpp liegen in der Datei nebeneinander; deshalb können sie mit nur einer einzigen (anstatt 3) Seek/Blockread-Kombination eingelesen werden. Da wir mit "Bit pro Pixel" wenig anfangen können teilen wir bpp anschließend durch 8 und erhalten "Bytes pro Pixel".

Hinweis: Für den, der es an dieser Stelle lieber objektorientiert mag, bieten sich natürlich auch Streams an. Allerdings ist dies für diesen konkreten Fall nicht von Belang.

function LoadTGA(const filename: string): TTGAFile;
var f: file; bytes: longword;
begin
  assign(f, filename);
  reset(f, 1);

  // type
  seek(f, 2);
  blockread(f, result.iType, 1);

  // w, h, bpp
  seek(f, 12);
  blockread(f, result.w, 5);
  result.bpp := result.bpp div 8;

  // data
  bytes := result.w * result.h * result.bpp;
  getmem(result.data, bytes);
  seek(f, 18);
  blockread(f, result.data^, bytes);

  close(f);
end;

Diese Funktion lädt alle benötigten Daten aus der Datei; reserviert auf Basis dieser Informationen genug Speicher für die Pixeldaten und liest sie dann ein. Der Pointer kann anschließend direkt in OpenGL verwendet werden, wenn die Implementierung die Formate GL_BGR und GL_BGRA unterstützt (je nach dem, ob "bpp" den Wert 3 oder 4 erhalten hat).

Datei rein - Textur raus

Die eigentliche Erzeugung des Texturobjektes gleicht dem "SDL-Weg". Nur wird hier unser eigenes Record anstatt einer SDL-Surface verwendet. Nicht vergessen den dynamisch reservierten Speicher für die Pixeldaten nach der Verwendung mit

FreeMem(TGARecord.data);

wieder freizugeben!

procedure TGATexture(const filename: string; var TexID: longword);
var
  tex: TTGAFile;
  glFormat: longword;
begin
  tex := LoadTGA(filename);
  if tex.iType = 2 then
  begin
    glGenTextures(1, @TexID);
    glBindTexture(GL_TEXTURE_2D, TexID);

    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    if tex.bpp = 3 then glFormat := GL_BGR
      else glFormat := GL_BGRA;

    glTexImage2D(GL_TEXTURE_2D, 0, tex.bpp, tex.w, tex.h,
      0, glFormat, GL_UNSIGNED_BYTE, tex.data);

    FreeMem(tex.data);
  end;
end;

Ich hoffe dieser Artikel war hilfreich (Ich will Feedback!) und wünsche euch viel Spaß und Erfolg bei euren zukünftigen Projekten.

Gruß Waran.