* refactored LoadTGA
authorBergmann89 <bergmann89@muo-game.de>
Wed, 6 Nov 2013 19:01:24 +0000 (20:01 +0100)
committerBergmann89 <bergmann89@muo-game.de>
Sun, 10 Nov 2013 13:14:56 +0000 (14:14 +0100)
glBitmap.pas

index 68d9cde..88724cf 100644 (file)
@@ -1004,6 +1004,7 @@ procedure glBitmapGetDefaultTextureWrap(var S, T, R: Cardinal);
 
 function glBitmapPosition(X: Integer = -1; Y: Integer = -1): TglBitmapPixelPosition;
 function glBitmapColorRec(const r, g, b, a: Cardinal): TglBitmapColorRec;
+function glBitmapColorRecCmp(const r1, r2: TglBitmapColorRec): Boolean;
 
 var
   glBitmapDefaultDeleteTextureOnFree: Boolean;
@@ -1052,6 +1053,7 @@ type
     fFormat: TglBitmapFormat;
     fWithAlpha: TglBitmapFormat;
     fWithoutAlpha: TglBitmapFormat;
+    fRGBInverted: TglBitmapFormat;
     fPixelSize: Single;
 
     fRange: TglBitmapColorRec;
@@ -1066,6 +1068,7 @@ type
     property Format:       TglBitmapFormat read fFormat;
     property WithAlpha:    TglBitmapFormat read fWithAlpha;
     property WithoutAlpha: TglBitmapFormat read fWithoutAlpha;
+    property RGBInverted:  TglBitmapFormat read fRGBInverted;
     property Components:   Integer         read GetComponents;
     property PixelSize:    Single          read fPixelSize;
 
@@ -1554,6 +1557,18 @@ begin
 end;
 
 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+function glBitmapColorRecCmp(const r1, r2: TglBitmapColorRec): Boolean;
+var
+  i: Integer;
+begin
+  result := false;
+  for i := 0 to high(r1.arr) do
+    if (r1.arr[i] <> r2.arr[i]) then
+      exit;
+  result := true;
+end;
+
+/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 function glBitmapShiftRec(const r, g, b, a: Byte): TShiftRec;
 begin
   result.r := r;
@@ -1565,7 +1580,7 @@ end;
 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes;
 begin
-  result := [ftDDS, ftTGA];
+  result := [ftDDS];
 
   if (aFormat in [
         //4 bbp
@@ -1586,6 +1601,22 @@ begin
         tfBGR10, tfBGR10A2, tfBGRA8]) then
     result := result + [ftBMP];
 
+  if (aFormat in [
+        //8 bpp
+        tfLuminance8, tfAlpha8,
+
+        //16 bpp
+        tfLuminance16, tfLuminance8Alpha8,
+        tfRGB5, tfRGB5A1, tfRGBA4,
+        tfBGR5, tfBGR5A1, tfBGRA4,
+
+        //24 bpp
+        tfRGB8, tfBGR8,
+
+        //32 bpp
+        tfRGB10A2, tfRGBA8, tfBGR10A2, tfBGRA8]) then
+    result := result + [ftTGA];
+
   //TODO Supported File Formats!
 
   (*
@@ -2204,6 +2235,7 @@ begin
   fFormat       := tfEmpty;
   fWithAlpha    := tfEmpty;
   fWithoutAlpha := tfEmpty;
+  fRGBInverted  := tfEmpty;
   fPixelSize    := 0.0;
 
   fglFormat         := 0;
@@ -2932,6 +2964,7 @@ begin
   fFormat           := tfRGB4;
   fWithAlpha        := tfRGBA4;
   fWithoutAlpha     := tfRGB4;
+  fRGBInverted      := tfBGR4;
   fRange.r          := $F;
   fRange.g          := $F;
   fRange.b          := $F;
@@ -2949,6 +2982,7 @@ begin
   fFormat           := tfR5G6B5;
   fWithAlpha        := tfRGBA4;
   fWithoutAlpha     := tfR5G6B5;
+  fRGBInverted      := tfB5G6R5;
   fRange.r          := $1F;
   fRange.g          := $3F;
   fRange.b          := $1F;
@@ -2966,6 +3000,7 @@ begin
   fFormat           := tfRGB5;
   fWithAlpha        := tfRGB5A1;
   fWithoutAlpha     := tfRGB5;
+  fRGBInverted      := tfBGR5;
   fRange.r          := $1F;
   fRange.g          := $1F;
   fRange.b          := $1F;
@@ -2983,6 +3018,7 @@ begin
   fFormat           := tfRGB8;
   fWithAlpha        := tfRGBA8;
   fWithoutAlpha     := tfRGB8;
+  fRGBInverted      := tfBGR8;
   fglInternalFormat := GL_RGB8;
 end;
 
@@ -2992,6 +3028,7 @@ begin
   fFormat           := tfRGB10;
   fWithAlpha        := tfRGB10A2;
   fWithoutAlpha     := tfRGB10;
+  fRGBInverted      := tfBGR10;
   fRange.r          := $3FF;
   fRange.g          := $3FF;
   fRange.b          := $3FF;
@@ -3009,6 +3046,7 @@ begin
   fFormat           := tfRGB12;
   fWithAlpha        := tfRGBA12;
   fWithoutAlpha     := tfRGB12;
+  fRGBInverted      := tfBGR12;
   fglInternalFormat := GL_RGB12;
 end;
 
@@ -3018,6 +3056,7 @@ begin
   fFormat           := tfRGB16;
   fWithAlpha        := tfRGBA16;
   fWithoutAlpha     := tfRGB16;
+  fRGBInverted      := tfBGR16;
   fglInternalFormat := GL_RGB16;
 end;
 
@@ -3027,6 +3066,7 @@ begin
   fFormat           := tfRGBA2;
   fWithAlpha        := tfRGBA2;
   fWithoutAlpha     := tfR3G3B2;
+  fRGBInverted      := tfBGRA2;
   fglInternalFormat := GL_RGBA2;
 end;
 
@@ -3036,6 +3076,7 @@ begin
   fFormat           := tfRGBA4;
   fWithAlpha        := tfRGBA4;
   fWithoutAlpha     := tfRGB4;
+  fRGBInverted      := tfBGRA4;
   fRange.r          := $F;
   fRange.g          := $F;
   fRange.b          := $F;
@@ -3055,6 +3096,7 @@ begin
   fFormat           := tfRGB5A1;
   fWithAlpha        := tfRGB5A1;
   fWithoutAlpha     := tfRGB5;
+  fRGBInverted      := tfBGR5A1;
   fRange.r          := $1F;
   fRange.g          := $1F;
   fRange.b          := $1F;
@@ -3074,6 +3116,7 @@ begin
   fFormat           := tfRGBA8;
   fWithAlpha        := tfRGBA8;
   fWithoutAlpha     := tfRGB8;
+  fRGBInverted      := tfBGRA8;
   fglInternalFormat := GL_RGBA8;
 end;
 
@@ -3083,6 +3126,7 @@ begin
   fFormat           := tfRGB10A2;
   fWithAlpha        := tfRGB10A2;
   fWithoutAlpha     := tfRGB10;
+  fRGBInverted      := tfBGR10A2;
   fRange.r          := $3FF;
   fRange.g          := $3FF;
   fRange.b          := $3FF;
@@ -3102,6 +3146,7 @@ begin
   fFormat           := tfRGBA12;
   fWithAlpha        := tfRGBA12;
   fWithoutAlpha     := tfRGB12;
+  fRGBInverted      := tfBGRA12;
   fglInternalFormat := GL_RGBA12;
 end;
 
@@ -3111,6 +3156,7 @@ begin
   fFormat           := tfRGBA16;
   fWithAlpha        := tfRGBA16;
   fWithoutAlpha     := tfRGB16;
+  fRGBInverted      := tfBGRA16;
   fglInternalFormat := GL_RGBA16;
 end;
 
@@ -3121,6 +3167,7 @@ begin
   fFormat           := tfBGR4;
   fWithAlpha        := tfBGRA4;
   fWithoutAlpha     := tfBGR4;
+  fRGBInverted      := tfRGB4;
   fRange.r          := $F;
   fRange.g          := $F;
   fRange.b          := $F;
@@ -3143,6 +3190,7 @@ begin
   fFormat           := tfB5G6R5;
   fWithAlpha        := tfBGRA4;
   fWithoutAlpha     := tfB5G6R5;
+  fRGBInverted      := tfR5G6B5;
   fRange.r          := $1F;
   fRange.g          := $3F;
   fRange.b          := $1F;
@@ -3154,9 +3202,6 @@ begin
   fglDataFormat     := GL_UNSIGNED_SHORT_5_6_5;
 end;
 
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 constructor TfdBGR5.Create;
 begin
   inherited Create;
@@ -3164,6 +3209,7 @@ begin
   fFormat           := tfBGR5;
   fWithAlpha        := tfBGR5A1;
   fWithoutAlpha     := tfBGR5;
+  fRGBInverted      := tfRGB5;
   fRange.r          := $1F;
   fRange.g          := $1F;
   fRange.b          := $1F;
@@ -3183,6 +3229,7 @@ begin
   fFormat           := tfBGR8;
   fWithAlpha        := tfBGRA8;
   fWithoutAlpha     := tfBGR8;
+  fRGBInverted      := tfRGB8;
   fglInternalFormat := GL_RGB8;
 end;
 
@@ -3192,6 +3239,7 @@ begin
   fFormat           := tfBGR10;
   fWithAlpha        := tfBGR10A2;
   fWithoutAlpha     := tfBGR10;
+  fRGBInverted      := tfRGB10;
   fRange.r          := $3FF;
   fRange.g          := $3FF;
   fRange.b          := $3FF;
@@ -3211,6 +3259,7 @@ begin
   fFormat           := tfBGR12;
   fWithAlpha        := tfBGRA12;
   fWithoutAlpha     := tfBGR12;
+  fRGBInverted      := tfRGB12;
   fglInternalFormat := GL_RGB12;
 end;
 
@@ -3220,6 +3269,7 @@ begin
   fFormat           := tfBGR16;
   fWithAlpha        := tfBGRA16;
   fWithoutAlpha     := tfBGR16;
+  fRGBInverted      := tfRGB16;
   fglInternalFormat := GL_RGB16;
 end;
 
@@ -3229,6 +3279,7 @@ begin
   fFormat           := tfBGRA2;
   fWithAlpha        := tfBGRA4;
   fWithoutAlpha     := tfBGR4;
+  fRGBInverted      := tfRGBA2;
   fglInternalFormat := GL_RGBA2;
 end;
 
@@ -3238,6 +3289,7 @@ begin
   fFormat           := tfBGRA4;
   fWithAlpha        := tfBGRA4;
   fWithoutAlpha     := tfBGR4;
+  fRGBInverted      := tfRGBA4;
   fRange.r          := $F;
   fRange.g          := $F;
   fRange.b          := $F;
@@ -3257,6 +3309,7 @@ begin
   fFormat           := tfBGR5A1;
   fWithAlpha        := tfBGR5A1;
   fWithoutAlpha     := tfBGR5;
+  fRGBInverted      := tfRGB5A1;
   fRange.r          := $1F;
   fRange.g          := $1F;
   fRange.b          := $1F;
@@ -3276,6 +3329,7 @@ begin
   fFormat           := tfBGRA8;
   fWithAlpha        := tfBGRA8;
   fWithoutAlpha     := tfBGR8;
+  fRGBInverted      := tfRGBA8;
   fglInternalFormat := GL_RGBA8;
 end;
 
@@ -3285,6 +3339,7 @@ begin
   fFormat           := tfBGR10A2;
   fWithAlpha        := tfBGR10A2;
   fWithoutAlpha     := tfBGR10;
+  fRGBInverted      := tfRGB10A2;
   fRange.r          := $3FF;
   fRange.g          := $3FF;
   fRange.b          := $3FF;
@@ -3304,6 +3359,7 @@ begin
   fFormat           := tfBGRA12;
   fWithAlpha        := tfBGRA12;
   fWithoutAlpha     := tfBGR12;
+  fRGBInverted      := tfRGBA12;
   fglInternalFormat := GL_RGBA12;
 end;
 
@@ -3313,6 +3369,7 @@ begin
   fFormat           := tfBGRA16;
   fWithAlpha        := tfBGRA16;
   fWithoutAlpha     := tfBGR16;
+  fRGBInverted      := tfRGBA16;
   fglInternalFormat := GL_RGBA16;
 end;
 
@@ -3531,9 +3588,6 @@ begin
         fColorTable[i].g := Round(((i shr Shift.g) and Range.g) / Range.g * 255);
         fColorTable[i].b := Round(((i shr Shift.b) and Range.b) / Range.b * 255);
         fColorTable[i].a := 0;
-
-        s := SysUtils.Format('%.2X%.2X%.2X' + sLineBreak, [fColorTable[i].r, fColorTable[i].g, fColorTable[i].b]);
-        fs.Write(s[1], Length(s));
       end;
     end;
   end;
@@ -4004,6 +4058,8 @@ procedure TglBitmap.LoadFromFile(const aFilename: String);
 var
   fs: TFileStream;
 begin
+  if not FileExists(aFilename) then
+    raise EglBitmapException.Create('file does not exist: ' + aFilename);
   fFilename := aFilename;
   fs := TFileStream.Create(fFilename, fmOpenRead);
   try
@@ -6422,29 +6478,43 @@ type
     ImageID: Byte;
     ColorMapType: Byte;
     ImageType: Byte;
-    ColorMapSpec: Array[0..4] of Byte;
+    //ColorMapSpec: Array[0..4] of Byte;
+    ColorMapStart: Word;
+    ColorMapLength: Word;
+    ColorMapEntrySize: Byte;
     OrigX: Word;
     OrigY: Word;
     Width: Word;
     Height: Word;
     Bpp: Byte;
-    ImageDes: Byte;
+    ImageDesc: Byte;
   end;
 
 const
-  TGA_UNCOMPRESSED_RGB = 2;
-  TGA_UNCOMPRESSED_GRAY = 3;
-  TGA_COMPRESSED_RGB = 10;
-  TGA_COMPRESSED_GRAY = 11;
+  TGA_UNCOMPRESSED_COLOR_TABLE =  1;
+  TGA_UNCOMPRESSED_RGB         =  2;
+  TGA_UNCOMPRESSED_GRAY        =  3;
+  TGA_COMPRESSED_COLOR_TABLE   =  9;
+  TGA_COMPRESSED_RGB           = 10;
+  TGA_COMPRESSED_GRAY          = 11;
+
+  TGA_NONE_COLOR_TABLE = 0;
+  TGA_COLOR_TABLE      = 1;
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 function TglBitmap.LoadTGA(const aStream: TStream): Boolean;
 var
   Header: TTGAHeader;
-  NewImage, pData: PByte;
-  StreamPos: Int64;
-  PixelSize, LineSize, YStart, YEnd, YInc: Integer;
-  Format: TglBitmapFormat;
+  ImageData: PByte;
+  StartPosition: Int64;
+  PixelSize, LineSize: Integer;
+  tgaFormat: TglBitmapFormat;
+  FormatDesc: TFormatDescriptor;
+  Counter: packed record
+    X, Y: packed record
+      low, high, dir: Integer;
+    end;
+  end;
 
 const
   CACHE_SIZE = $4000;
@@ -6452,195 +6522,233 @@ const
   ////////////////////////////////////////////////////////////////////////////////////////
   procedure ReadUncompressed;
   var
-    RowSize: Integer;
+    i, j: Integer;
+    buf, tmp1, tmp2: PByte;
   begin
-    RowSize := Header.Width * PixelSize;
-    // copy line by line
-    while YStart <> YEnd + YInc do begin
-      pData := NewImage;
-      Inc(pData, YStart * LineSize);
-      aStream.Read(pData^, RowSize);
-      Inc(YStart, YInc);
+    buf := nil;
+    if (Counter.X.dir < 0) then
+      buf := GetMem(LineSize);
+    try
+      while (Counter.Y.low <> Counter.Y.high + counter.Y.dir) do begin
+        tmp1 := ImageData + (Counter.Y.low * LineSize); //pointer to LineStart
+        if (Counter.X.dir < 0) then begin               //flip X
+          aStream.Read(buf^, LineSize);
+          tmp2 := buf + LineSize - PixelSize;           //pointer to last pixel in line
+          for i := 0 to Header.Width-1 do begin         //for all pixels in line
+            for j := 0 to PixelSize-1 do begin          //for all bytes in pixel
+              tmp1^ := tmp2^;
+              inc(tmp1);
+              inc(tmp2);
+            end;
+            dec(tmp2, 2*PixelSize);                     //move 2 backwards, because j-loop moved 1 forward
+          end;
+        end else
+          aStream.Read(tmp1^, LineSize);
+        inc(Counter.Y.low, Counter.Y.dir);              //move to next line index
+      end;
+    finally
+      if Assigned(buf) then
+        FreeMem(buf);
     end;
   end;
 
   ////////////////////////////////////////////////////////////////////////////////////////
   procedure ReadCompressed;
-  var
-    HeaderWidth, HeaderHeight: Integer;
-    LinePixelsRead, ImgPixelsRead, ImgPixelsToRead: Integer;
-
-    Cache: PByte;
-    CacheSize, CachePos: Integer;
-
-    Temp: Byte;
-    TempBuf: Array [0..15] of Byte;
-
-    PixelRepeat: Boolean;
-    PixelToRead, TempPixels: Integer;
 
     /////////////////////////////////////////////////////////////////
+    var
+      TmpData: PByte;
+      LinePixelsRead: Integer;
     procedure CheckLine;
     begin
-      if LinePixelsRead >= HeaderWidth then begin
+      if (LinePixelsRead >= Header.Width) then begin
         LinePixelsRead := 0;
-        pData := NewImage;
-        Inc(YStart, YInc);
-        Inc(pData, YStart * LineSize);
+        inc(Counter.Y.low, Counter.Y.dir);                //next line index
+        TmpData := ImageData + Counter.Y.low * LineSize;  //set line
+        if (Counter.X.dir < 0) then                       //if x flipped then
+          TmpData := TmpData + LineSize - PixelSize;      //set last pixel
       end;
     end;
 
     /////////////////////////////////////////////////////////////////
+    var
+      Cache: PByte;
+      CacheSize, CachePos: Integer;
     procedure CachedRead(out Buffer; Count: Integer);
     var
       BytesRead: Integer;
     begin
-      if (CachePos + Count) > CacheSize then begin
+      if (CachePos + Count > CacheSize) then begin
+        //if buffer overflow save non read bytes
         BytesRead := 0;
-
-        // Read Data
-        if CacheSize - CachePos > 0 then begin
+        if (CacheSize - CachePos > 0) then begin
           BytesRead := CacheSize - CachePos;
-          Move(pByteArray(Cache)^[CachePos], Buffer, BytesRead);
-          Inc(CachePos, BytesRead);
+          Move(PByteArray(Cache)^[CachePos], Buffer, BytesRead);
+          inc(CachePos, BytesRead);
         end;
 
-        // Reload Data
+        //load cache from file
         CacheSize := Min(CACHE_SIZE, aStream.Size - aStream.Position);
         aStream.Read(Cache^, CacheSize);
         CachePos := 0;
 
-        // Read else
-        if Count - BytesRead > 0 then begin
-          Move(pByteArray(Cache)^[CachePos], TByteArray(Buffer)[BytesRead], Count - BytesRead);
-          Inc(CachePos, Count - BytesRead);
+        //read rest of requested bytes
+        if (Count - BytesRead > 0) then begin
+          Move(PByteArray(Cache)^[CachePos], TByteArray(Buffer)[BytesRead], Count - BytesRead);
+          inc(CachePos, Count - BytesRead);
         end;
       end else begin
-        Move(pByteArray(Cache)^[CachePos], Buffer, Count);
-        Inc(CachePos, Count);
+        //if no buffer overflow just read the data
+        Move(PByteArray(Cache)^[CachePos], Buffer, Count);
+        inc(CachePos, Count);
+      end;
+    end;
+
+    procedure PixelToBuffer(const aData: PByte; var aBuffer: PByte);
+    begin
+      case PixelSize of
+        1: begin
+          aBuffer^ := aData^;
+          inc(aBuffer, Counter.X.dir);
+        end;
+        2: begin
+          PWord(aBuffer)^ := PWord(aData)^;
+          inc(aBuffer, 2 * Counter.X.dir);
+        end;
+        3: begin
+          PByteArray(aBuffer)^[0] := PByteArray(aData)^[0];
+          PByteArray(aBuffer)^[1] := PByteArray(aData)^[1];
+          PByteArray(aBuffer)^[2] := PByteArray(aData)^[2];
+          inc(aBuffer, 3 * Counter.X.dir);
+        end;
+        4: begin
+          PCardinal(aBuffer)^ := PCardinal(aData)^;
+          inc(aBuffer, 4 * Counter.X.dir);
+        end;
       end;
     end;
 
+  var
+    TotalPixelsToRead, TotalPixelsRead: Integer;
+    Temp: Byte;
+    buf: array [0..3] of Byte; //1 pixel is max 32bit long
+    PixelRepeat: Boolean;
+    PixelsToRead, PixelCount: Integer;
   begin
     CacheSize := 0;
-    CachePos := 0;
+    CachePos  := 0;
 
-    HeaderWidth := Header.Width;
-    HeaderHeight := Header.Height;
+    TotalPixelsToRead := Header.Width * Header.Height;
+    TotalPixelsRead   := 0;
+    LinePixelsRead    := 0;
 
-    GetMem(Cache, CACHE_SIZE); // 16K Buffer
+    GetMem(Cache, CACHE_SIZE);
     try
-      ImgPixelsToRead := HeaderWidth * HeaderHeight;
-      ImgPixelsRead := 0;
-      LinePixelsRead := 0;
+      TmpData := ImageData + Counter.Y.low * LineSize;  //set line
+      if (Counter.X.dir < 0) then                       //if x flipped then
+        TmpData := TmpData + LineSize - PixelSize;      //set last pixel
 
-      pData := NewImage;
-      Inc(pData, YStart * LineSize);
-
-      // Read until all Pixels
       repeat
+        //read CommandByte
         CachedRead(Temp, 1);
-
-        PixelRepeat := Temp and $80 > 0;
-        PixelToRead := (Temp and $7F) + 1;
-
-        Inc(ImgPixelsRead, PixelToRead);
-
-        if PixelRepeat then begin
-          // repeat one pixel x times
-          CachedRead(TempBuf[0], PixelSize);
-
-          // repeat Pixel
-          while PixelToRead > 0 do begin
-            CheckLine;
-
-            TempPixels := HeaderWidth - LinePixelsRead;
-            if PixelToRead < TempPixels then
-              TempPixels := PixelToRead;
-
-            Inc(LinePixelsRead, TempPixels);
-            Dec(PixelToRead, TempPixels);
-
-            while TempPixels > 0 do begin
-              case PixelSize of
-                1: begin
-                  pData^ := TempBuf[0];
-                  Inc(pData);
-                end;
-                2: begin
-                  pWord(pData)^ := pWord(@TempBuf[0])^;
-                  Inc(pData, 2);
-                end;
-                3: begin
-                  pWord(pData)^ := pWord(@TempBuf[0])^;
-                  Inc(pData, 2);
-                  pData^ := TempBuf[2];
-                  Inc(pData);
-                end;
-                4: begin
-                  pDWord(pData)^ := pDWord(@TempBuf[0])^;
-                  Inc(pData, 4);
-                end;
-              end;
-              Dec(TempPixels);
-            end;
-          end;
-        end else begin
-          // copy x pixels
-          while PixelToRead > 0 do begin
-            CheckLine;
-            TempPixels := HeaderWidth - LinePixelsRead;
-            if PixelToRead < TempPixels then
-              TempPixels := PixelToRead;
-            CachedRead(pData^, PixelSize * TempPixels);
-            Inc(pData, PixelSize * TempPixels);
-            Inc(LinePixelsRead, TempPixels);
-            Dec(PixelToRead, TempPixels);
+        PixelRepeat  := (Temp and $80) > 0;
+        PixelsToRead := (Temp and $7F) + 1;
+        inc(TotalPixelsRead, PixelsToRead);
+
+        if PixelRepeat then
+          CachedRead(buf[0], PixelSize);
+        while (PixelsToRead > 0) do begin
+          CheckLine;
+          PixelCount := Min(Header.Width - LinePixelsRead, PixelsToRead); //max read to EOL or EOF
+          while (PixelCount > 0) do begin
+            if not PixelRepeat then
+              CachedRead(buf[0], PixelSize);
+            PixelToBuffer(@buf[0], TmpData);
+            inc(LinePixelsRead);
+            dec(PixelsToRead);
+            dec(PixelCount);
           end;
         end;
-      until ImgPixelsRead >= ImgPixelsToRead;
+      until (TotalPixelsRead >= TotalPixelsToRead);
     finally
-      FreeMem(Cache)
+      FreeMem(Cache);
     end;
   end;
 
+  function IsGrayFormat: Boolean;
+  begin
+    result := Header.ImageType in [TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_GRAY];
+  end;
+
 begin
   result := false;
 
   // reading header to test file and set cursor back to begin
-  StreamPos := aStream.Position;
+  StartPosition := aStream.Position;
   aStream.Read(Header, SizeOf(Header));
 
   // no colormapped files
-  if (Header.ColorMapType = 0) then begin
-    if Header.ImageType in [TGA_UNCOMPRESSED_RGB, TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_RGB, TGA_COMPRESSED_GRAY] then begin
+  if (Header.ColorMapType = TGA_NONE_COLOR_TABLE) and (Header.ImageType in [
+    TGA_UNCOMPRESSED_RGB, TGA_UNCOMPRESSED_GRAY, TGA_COMPRESSED_RGB, TGA_COMPRESSED_GRAY]) then
+  begin
+    try
+      if Header.ImageID <> 0 then       // skip image ID
+        aStream.Position := aStream.Position + Header.ImageID;
+
       case Header.Bpp of
-        //TODO 8: Format := tfAlpha8;
-        16: Format := tfLuminance8Alpha8;
-        24: Format := tfBGR8;
-        32: Format := tfBGRA8;
-      else
-        raise EglBitmapException.Create('LoadTga - unsupported BitsPerPixel found.');
+         8: if IsGrayFormat then case (Header.ImageDesc and $F) of
+               0: tgaFormat := tfLuminance8;
+               8: tgaFormat := tfAlpha8;
+            end;
+
+        16: if IsGrayFormat then case (Header.ImageDesc and $F) of
+               0: tgaFormat := tfLuminance16;
+               8: tgaFormat := tfLuminance8Alpha8;
+            end else case (Header.ImageDesc and $F) of
+               0: tgaFormat := tfBGR5;
+               1: tgaFormat := tfBGR5A1;
+               4: tgaFormat := tfBGRA4;
+            end;
+
+        24: if not IsGrayFormat then case (Header.ImageDesc and $F) of
+               0: tgaFormat := tfBGR8;
+            end;
+
+        32: if not IsGrayFormat then case (Header.ImageDesc and $F) of
+               2: tgaFormat := tfBGR10A2;
+               8: tgaFormat := tfBGRA8;
+            end;
       end;
 
-      // skip image ID
-      if Header.ImageID <> 0 then
-        aStream.Position := aStream.Position + Header.ImageID;
+      if (tgaFormat = tfEmpty) then
+        raise EglBitmapException.Create('LoadTga - unsupported format');
 
-      PixelSize := TFormatDescriptor.Get(Format).GetSize(1, 1);
-      LineSize  := Trunc(Header.Width * PixelSize);
+      FormatDesc := TFormatDescriptor.Get(tgaFormat);
+      PixelSize  := FormatDesc.GetSize(1, 1);
+      LineSize   := FormatDesc.GetSize(Header.Width, 1);
 
-      GetMem(NewImage, LineSize * Header.Height);
+      GetMem(ImageData, LineSize * Header.Height);
       try
+        //column direction
+        if ((Header.ImageDesc and (1 shl 4)) > 0) then begin
+          Counter.X.low  := Header.Height-1;;
+          Counter.X.high := 0;
+          Counter.X.dir  := -1;
+        end else begin
+          Counter.X.low  := 0;
+          Counter.X.high := Header.Height-1;
+          Counter.X.dir  := 1;
+        end;
+
         // Row direction
-        if (Header.ImageDes and $20 > 0) then begin
-          YStart := 0;
-          YEnd := Header.Height -1;
-          YInc := 1;
+        if ((Header.ImageDesc and (1 shl 5)) > 0) then begin
+          Counter.Y.low  := 0;
+          Counter.Y.high := Header.Height-1;
+          Counter.Y.dir  := 1;
         end else begin
-          YStart := Header.Height -1;
-          YEnd := 0;
-          YInc := -1;
+          Counter.Y.low  := Header.Height-1;;
+          Counter.Y.high := 0;
+          Counter.Y.dir  := -1;
         end;
 
         // Read Image
@@ -6651,97 +6759,106 @@ begin
             ReadCompressed;
         end;
 
-        SetDataPointer(NewImage, Format, Header.Width, Header.Height);
+        SetDataPointer(ImageData, tgaFormat, Header.Width, Header.Height);
         result := true;
       except
-        FreeMem(NewImage);
+        FreeMem(ImageData);
         raise;
       end;
-    end
-      else aStream.Position := StreamPos;
+    finally
+      aStream.Position := StartPosition;
+    end;
   end
-    else aStream.Position := StreamPos;
+    else aStream.Position := StartPosition;
 end;
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
 procedure TglBitmap.SaveTGA(const aStream: TStream);
 var
   Header: TTGAHeader;
-  Size: Integer;
-  pTemp: pByte;
+  LineSize, Size, x, y: Integer;
+  Pixel: TglBitmapPixelData;
+  LineBuf, SourceData, DestData: PByte;
+  SourceMD, DestMD: Pointer;
   FormatDesc: TFormatDescriptor;
-
-  procedure ConvertData(pTemp: pByte);
-  var
-    Idx, PixelSize: Integer;
-    Temp: byte;
-  begin
-    PixelSize := fPixelSize;
-    for Idx := 1 to Height * Width do begin
-      Temp := pByteArray(pTemp)^[2];
-      pByteArray(pTemp)^[2] := pByteArray(pTemp)^[0];
-      pByteArray(pTemp)^[0] := Temp;
-      Inc(pTemp, PixelSize);
-    end;
-  end;
-
+  Converter: TFormatDescriptor;
 begin
   if not (ftTGA in FormatGetSupportedFiles(Format)) then
     raise EglBitmapUnsupportedFormatFormat.Create('SaveTGA - ' + UNSUPPORTED_FORMAT);
 
+  //prepare header
   FillChar(Header, SizeOf(Header), 0);
-  case Format of
-    //TODO ifAlpha8, ifLuminance8, ifDepth8: begin
-    tfLuminance8: begin
-      Header.ImageType := TGA_UNCOMPRESSED_GRAY;
-      Header.Bpp := 8;
-    end;
-    tfLuminance8Alpha8: begin
-      Header.ImageType := TGA_UNCOMPRESSED_GRAY;
-      Header.Bpp := 16;
-    end;
-    tfRGB8, tfBGR8: begin
-      Header.ImageType := TGA_UNCOMPRESSED_RGB;
-      Header.Bpp := 24;
-    end;
-    tfRGBA8, tfBGRA8: begin
-      Header.ImageType := TGA_UNCOMPRESSED_RGB;
-      Header.Bpp := 32;
-    end;
-  else
-    raise EglBitmapUnsupportedFormatFormat.Create('SaveTGA - ' + UNSUPPORTED_FORMAT);
-  end;
 
-  Header.Width    := Width;
-  Header.Height   := Height;
-  Header.ImageDes := $20;
-  FormatDesc      := TFormatDescriptor.Get(Format);
+  //set ImageType
+  if (Format in [tfLuminance8, tfLuminance6Alpha2, tfLuminance4Alpha4, tfAlpha8,
+                 tfLuminance16, tfLuminance12Alpha4, tfLuminance8Alpha8]) then
+    Header.ImageType := TGA_UNCOMPRESSED_GRAY
+  else
+    Header.ImageType := TGA_UNCOMPRESSED_RGB;
+
+  //set BitsPerPixel
+  if (Format in [tfLuminance8, tfLuminance6Alpha2, tfLuminance4Alpha4, tfAlpha8]) then
+    Header.Bpp := 8
+  else if (Format in [tfLuminance16, tfLuminance12Alpha4, tfLuminance8Alpha8,
+                      tfRGB5, tfBGR5, tfRGB5A1, tfBGR5A1, tfRGBA4, tfBGRA4]) then
+    Header.Bpp := 16
+  else if (Format in [tfBGR8, tfRGB8]) then
+    Header.Bpp := 24
+  else
+    Header.Bpp := 32;
 
-  if FormatDesc.HasAlpha then
-    Header.ImageDes := Header.ImageDes or $08;
+  //set AlphaBitCount
+  case Format of
+    tfRGB5A1, tfBGR5A1:
+      Header.ImageDesc := 1 and $F;
+    tfRGB10A2, tfBGR10A2:
+      Header.ImageDesc := 2 and $F;
+    tfRGBA4, tfBGRA4:
+      Header.ImageDesc := 4 and $F;
+    tfAlpha8, tfLuminance8Alpha8, tfRGBA8, tfBGRA8:
+      Header.ImageDesc := 8 and $F;
+  end;
+
+  Header.Width     := Width;
+  Header.Height    := Height;
+  Header.ImageDesc := Header.ImageDesc or $20; //flip y
   aStream.Write(Header, SizeOf(Header));
 
   // convert RGB(A) to BGR(A)
-  Size := FormatDesc.GetSize(Dimension);
-  if Format in [tfRGB8, tfRGBA8] then begin
-    GetMem(pTemp, Size);
-  end else
-    pTemp := Data;
-
-  try
-    // convert data
-    if Format in [tfRGB8, tfRGBA8] then begin
-      Move(Data^, pTemp^, Size);
-      ConvertData(pTemp);
+  Converter  := nil;
+  FormatDesc := TFormatDescriptor.Get(Format);
+  Size       := FormatDesc.GetSize(Dimension);
+  if Format in [tfRGB5, tfRGB5A1, tfRGBA4, tfRGB8, tfRGB10A2, tfRGBA8] then begin
+    if (FormatDesc.RGBInverted = tfEmpty) then
+      raise EglBitmapException.Create('inverted RGB format is empty');
+    Converter := TFormatDescriptor.Get(FormatDesc.RGBInverted);
+    if not glBitmapColorRecCmp(Converter.Range, FormatDesc.Range) or
+       (Converter.PixelSize <> FormatDesc.PixelSize) then
+      raise EglBitmapException.Create('invalid inverted RGB format');
+  end;
+
+  if Assigned(Converter) then begin
+    LineSize := FormatDesc.GetSize(Width, 1);
+    LineBuf  := GetMem(LineSize);
+    SourceMD := FormatDesc.CreateMappingData;
+    DestMD   := Converter.CreateMappingData;
+    try
+      SourceData := Data;
+      for y := 0 to Height-1 do begin
+        DestData := LineBuf;
+        for x := 0 to Width-1 do begin
+          FormatDesc.Unmap(SourceData, Pixel, SourceMD);
+          Converter.Map(Pixel, DestData, DestMD);
+        end;
+        aStream.Write(LineBuf^, LineSize);
+      end;
+    finally
+      FreeMem(LineBuf);
+      FormatDesc.FreeMappingData(SourceMD);
+      FormatDesc.FreeMappingData(DestMD);
     end;
-
-    // write data
-    aStream.Write(pTemp^, Size);
-  finally
-    // free tempdata
-    if Format in [tfRGB8, tfRGBA8] then
-      FreeMem(pTemp);
-  end;
+  end else
+    aStream.Write(Data^, Size);
 end;
 
 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////