{
  TODO fix loader for android
}
{$MODE OBJFPC}
{$MODESWITCH AUTODEREF}
unit dtga;

interface

uses
  dtypes,
  dstrnum,
  dcolors,
  dstream,
  dstream_op,
  dtexture2d,
  dtga_reader;

type
TTexture2dFormatTga = object(TTexture2dFormat)
  function ReadHeaderAndCheckSupported(Stream: PStream; var Header: TTgaHeader): Boolean;
  function CheckFormat(Stream: PStream): Boolean; virtual;
  function Load(Stream: PStream; var Texture: TTexture2dRgba): Boolean; virtual;

  procedure SwapVertical(var T: TTexture2dRgba);
end;

implementation

function TTexture2dFormatTga.ReadHeaderAndCheckSupported(Stream: PStream; var Header: TTgaHeader): Boolean;
var
  TgaReader: TTgaReader;
begin
  TgaReader.Init(Stream);
  Result := TgaReader.Valid;
  if TgaReader.Valid then
    Header := TgaReader.Header^;
  TgaReader.Done;
end;

function TTexture2dFormatTga.CheckFormat(Stream: PStream): Boolean;
var
  Header: TTgaHeader;
begin
  Result := ReadHeaderAndCheckSupported(Stream, Header);
end;

function TTexture2dFormatTga.Load(Stream: PStream; var Texture: TTexture2dRgba): Boolean;
label
  FAILED;
type
  TArray4 = array[0..3] of Byte;
  PArray4 = ^TArray4;
  TArray3 = array[0..2] of Byte;
  PArray3 = ^TArray3;
var
  Reader: TTgaReader;
  Inv: Boolean;
  I, J, Y: Integer;
  R, G, B, A: Byte;
  P4: PArray4;
  P3: PArray3;
  Buffer: array of array[0..2] of Byte;
  Temp: Byte;
  Curr, Count: LongInt;
  BytesPerPixel: LongInt;
  Pixel: TArray4;
begin
  Result := False;

  Reader.Init(Stream);
  if not Reader.Valid then
    goto FAILED;

  Inv := Reader.NeedVerticalSwap;
  Texture.InitRGBA(Reader.Width, Reader.Height);
  if (Reader.Header.ImageType = TGA_IMAGE_TYPE_RGB) and
     (Reader.Header.PixelDepth = 32) then begin
    for J := 0 to Reader.Height - 1 do begin
      if Inv then begin
        Y := Reader.Height - J - 1;
      end else
        Y := J;
      P4 := PArray4(PByte(PArray4(Texture.Data)) + 4 * Reader.Width * Y);
      Stream.Read(P4, 4 * Reader.Width);
      while P4 < PArray4(PByte(PArray4(Texture.Data)) + 4 * Reader.Width * (Y + 1)) do begin
        BGRAToRGBA(P4);
        Inc(P4);
      end;
    end;
  end else if (Reader.Header.ImageType = TGA_IMAGE_TYPE_RGB) and
              (Reader.Header.PixelDepth = 24) then begin
    SetLength(Buffer, Reader.Width);
    for J := 0 to Reader.Height - 1 do begin
      Stream.Read(@Buffer[0][0], 3 * Reader.Width);
      if Inv then begin
        Y := Reader.Height - J - 1;
      end else
        Y := J;
      P4 := PArray4(PByte(PArray4(Texture.Data)) + 4 * Reader.Width * Y);
      P3 := @Buffer[0][0];
      while P4 < PArray4(PByte(PArray4(Texture.Data)) + 4 * Reader.Width * (Y + 1)) do begin
        P4^[0] := P3^[2];
        P4^[1] := P3^[1];
        P4^[2] := P3^[0];
        P4^[3] := 255;
        Inc(P4);
        Inc(P3);
      end;
    end;
  end else if (Reader.Header.ImageType = TGA_IMAGE_TYPE_RUNLENGTH_RGB) and
              (Reader.Header.PixelDepth = 32) then begin
    // rle 32-bit image
    Curr := 0;
    BytesPerPixel := 4;
    Count := BytesPerPixel * Reader.Width * Reader.Height;
    while Curr < Count do begin
      B := Reader.Stream.ReadByte;
      if (B and TGA_RLE_MASK) <> 0 then begin
        // run-length packet
        Reader.Stream.Read(@Pixel[0], BytesPerPixel);
        BGRAToRGBA(@Pixel[0]);
        for I := 0 to B - TGA_RLE_MASK do begin
          P4 := PArray4(PByte(PArray4(Texture.Data)) + Curr);
          Move(Pixel[0], P4^, BytesPerPixel);
          Inc(Curr, BytesPerPixel);
        end;
      end else begin
        // raw packet
        P4 := PArray4(PByte(PArray4(Texture.Data)) + Curr);
        Reader.Stream.Read(P4, (B + 1) * BytesPerPixel);
        for I := 0 to (B + 1) - 1 do begin
          BGRAToRGBA(P4);
          Inc(P4);
        end;
        Inc(Curr, BytesPerPixel * (B + 1));
      end;
    end;
    if Inv then
      SwapVertical(Texture);
  end else if (Reader.Header.ImageType = TGA_IMAGE_TYPE_RUNLENGTH_RGB) and
              (Reader.Header.PixelDepth = 24) then begin
    // rle 24-bit image
    Curr := 0;
    BytesPerPixel := 3;
    Count := 4 * Reader.Width * Reader.Height;
    while Curr < Count do begin
      B := Reader.Stream.ReadByte;
      if (B and TGA_RLE_MASK) <> 0 then begin
        // run-length packet
        Reader.Stream.Read(@Pixel[0], BytesPerPixel);
        Pixel[3] := 255;
        BGRAToRGBA(@Pixel[0]);
        for I := 0 to B - TGA_RLE_MASK do begin
          P4 := PArray4(PByte(PArray4(Texture.Data)) + Curr);
          Move(Pixel[0], P4^, 4);
          Inc(Curr, 4);
        end;
      end else begin
        // raw packet
        P4 := PArray4(PByte(PArray4(Texture.Data)) + Curr);
        for I := 0 to (B + 1) - 1 do begin
          Reader.Stream.Read(P4, BytesPerPixel);
          P4^[3] := 255;
          BGRAToRGBA(P4);
          Inc(P4);
        end;
        Inc(Curr, 4 * (B + 1));
      end;
    end;
    if Inv then
      SwapVertical(Texture);
  end else begin
    // unsupported format
    Texture.Done;
    goto FAILED;
  end;

  Reader.Done;
  Result := True;
  Exit;

FAILED:
  Reader.Done;
  Exit;
end;

procedure TTexture2dFormatTga.SwapVertical(var T: TTexture2dRgba);
var
  I: LongInt;
  Buffer: array of Byte;
  A, B: Pointer;
  BytesInLine: LongInt;
begin
  BytesInLine := 4 * T.Width;
  SetLength(Buffer, BytesInLine);
  for I := 0 to (T.Height div 2) - 1 do begin
    A := PByte(T.Data) + I * BytesInLine;
    B := PByte(T.Data) + (T.Height - 1 - I) * BytesInLine;
    Move(A^, Buffer[0], BytesInLine);
    Move(B^, A^, BytesInLine);
    Move(Buffer[0], B^, BytesInLine);
  end;
end;

var
  Format: TTexture2dFormatTga;
initialization
  Format.Init;
  RegistTexture2dFormat(@Format);
end.
