unit OniImgClass;

interface

uses Math, Dialogs, Types, SysUtils, Classes, Data, ConnectionManager, TypeDefs,
  Imaging, ImagingTypes;

type
  TImgDataType = set of (DT_OniReverted, DT_Oni, DT_Decoded32);


type
  TOniImage = class
  private
    FLoaded:    Boolean;
    FDataType:  TImgDataType;
    FData:      TByteData;
    FWidth, FHeight: Word;
    FDepth:     Byte;
    FStoreType: Byte;

    FImage:     TImageData;

    function ResizeImage(oldx, oldy: Integer; img: TByteData): TByteData;
    procedure RevertImage;
    procedure DecompressImage;
  protected
  public
    property Image:     TImageData   Read FImage     Write FImage;
    property Loaded:    Boolean      Read FLoaded    Write FLoaded;
    property DataType:  TImgDataType Read FDataType  Write FDataType;
    property Width:     Word         Read FWidth     Write FWidth;
    property Height:    Word         Read FHeight    Write FHeight;
    property Depth:     Byte         Read FDepth     Write FDepth;
    property StoreType: Byte         Read FStoreType Write FStoreType;
    property Data:      TByteData    Read FData      Write FData;

    constructor Create;
    function Load(ConnectionID, FileID: Integer): Boolean;
    function LoadFromPSpc(ConnectionID, FileID: Integer): Boolean;
    function LoadFromTXMP(ConnectionID, FileID: Integer): Boolean;
    function LoadFromTXMB(ConnectionID, FileID: Integer): Boolean;
    function GetImgSize(w,h, storetype: Integer): Integer;
    function GetImageDataSize(fading: Boolean): Integer;

    procedure DecodeImageTo32bit;

    procedure GetAsData(var Target: TStream); overload;
    procedure GetAsData(var Target: TByteData); overload;
    procedure GetAs32bit(var Target: TStream); overload;
    procedure GetAs32bit(var Target: TByteData); overload;
    procedure GetAsBMP(var Target: TStream); overload;
    procedure GetAsBMP(var Target: TByteData); overload;
    function LoadFromBMP(filename: String): Boolean;
    function WriteToBMP(filename: String): Boolean;
    function GetMipMappedImage(var Target: TStream): Boolean; overload;
    function GetMipMappedImage(var Target: TByteData): Boolean; overload;
  published
  end;


implementation

//uses Functions;
uses Img_DDSTypes;


constructor TOniImage.Create;
begin
  Self.FLoaded   := False;
  Self.FDataType := [];
  SetLength(Self.FData, 0);
  Self.FWidth     := 0;
  Self.FHeight    := 0;
  Self.FDepth     := 0;
  Self.FStoreType := 0;

  InitImage(FImage);
end;




function TOniImage.ResizeImage(oldx, oldy: Integer; img: TByteData): TByteData;
var
  i, j: Integer;
  col, row, row_orig: Integer;
begin
  SetLength(Result, (oldx div 2) * (oldy div 2) * (Self.FDepth div 8));
  row_orig := 0;
  row      := 0;
  col      := 0;
  for i := 0 to (oldx * oldy) - 1 do
  begin
    if ((i mod oldx) = 0) and (i > 0) then
    begin
      Inc(row_orig);
      if (row_orig mod 2) = 0 then
      begin
        Inc(row);
        col := 0;
      end;
    end;
    if (row_orig mod 2) = 0 then
    begin
      if (i mod 2) = 0 then
      begin
        for j := 0 to (Self.FDepth div 8) - 1 do
          Result[((row * (oldx div 2)) + col) * (Self.FDepth div 8) + j] :=
            img[(i * (Self.FDepth div 8)) + j];
        Inc(col);
      end;
    end;
  end;
end;




procedure TOniImage.RevertImage;
var
  x, y, i: Integer;
  tempd:   TByteData;
begin
  SetLength(tempd, Self.FWidth * Self.FHeight * (Self.FDepth div 8));
  for y := 0 to Self.FHeight - 1 do
    for x := 0 to Self.FWidth - 1 do
      for i := 0 to (Self.FDepth div 8) - 1 do
        tempd[((Self.FWidth * (Self.FHeight - 1 - y) + x) * (Self.FDepth div 8)) + i] :=
          Self.FData[(Self.FWidth * y + x) * (Self.FDepth div 8) + i];
  for x := 0 to High(tempd) do
    Self.FData[x] := tempd[x];
  if DT_OniReverted in Self.FDataType then
    Self.FDataType := Self.FDataType - [DT_OniReverted]
  else
    Self.FDataType := Self.FDataType + [DT_OniReverted];
end;




procedure TOniImage.DecodeImageTo32bit;
var
  x, y:  Integer;
  tempd: TByteData;
begin
  if not (DT_Decoded32 in Self.FDataType) then
  begin
    SetLength(tempd, Self.FWidth * Self.FHeight * 4);
    case Self.FStoreType of
      0: // 16bit, RGB444, A4?
      begin
        for y := 0 to Self.FHeight - 1 do
        begin
          for x := 0 to Self.FWidth - 1 do
          begin
            tempd[((Self.FWidth * y + x) * 4) + 0] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $000F) / $000F * 255);
            tempd[((Self.FWidth * y + x) * 4) + 1] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $00F0) / $00F0 * 255);
            tempd[((Self.FWidth * y + x) * 4) + 2] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $0F00) / $0F00 * 255);
            tempd[((Self.FWidth * y + x) * 4) + 3] := 0;
          end;
        end;
      end;
      1, 2: // 16bit, RGB555, A1?
      begin
        for y := 0 to Self.FHeight - 1 do
        begin
          for x := 0 to Self.FWidth - 1 do
          begin
            tempd[((Self.FWidth * y + x) * 4) + 0] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $001F) / $001F * 255);
            tempd[((Self.FWidth * y + x) * 4) + 1] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $03E0) / $03E0 * 255);
            tempd[((Self.FWidth * y + x) * 4) + 2] :=
              Round(((Self.FData[(Self.FWidth * y + x) * 2] + Self.FData[(Self.FWidth * y + x) * 2 + 1] * 256) and
              $7C00) / $7C00 * 255);
            tempd[((Self.FWidth * y + x) * 4) + 3] := 0;
          end;
        end;
      end;
      8: // 32bit, RGB888, A8?
      begin end;
      9: // Compressed, RGB565
      begin
        DecompressImage;
      end;
    end;
    Self.FDepth := 32;
    if (Self.FStoreType <> 9) and (Self.FStoreType <> 8) then
    begin
      SetLength(Self.FData, Length(tempd));
      for x := 0 to High(tempd) do
        Self.FData[x] := tempd[x];
    end;
    Self.FStoreType := 8;
    if DT_Oni in Self.FDataType then
      Self.FDataType := Self.FDataType - [DT_Oni];
    Self.FDataType := Self.FDataType + [DT_Decoded32];
  end;
  if DT_OniReverted in Self.FDataType then
    Self.RevertImage;
end;




procedure TOniImage.DecompressImage;
type
  Tcolor = record
    RGBb: Byte;
    RGBg: Byte;
    RGBr: Byte;
    RGBa: Byte;
  end;
var
  i, j, x, y: Integer;
  color:      array[1..4] of Tcolor;
  pixel:      array[1..16] of Byte;
  tempd:      TByteData;
begin
  x := 0;
  y := 0;
  SetLength(tempd, Self.FWidth * Self.FHeight * 4);
  for i := 0 to ((Self.FWidth * Self.FHeight) div 16) - 1 do
  begin
    Color[1].RGBb := Round(((Self.FData[(i * 8) + 0] + Self.FData[(i * 8) + 1] * 256) and $001F) /
      $001F * 255);
    Color[1].RGBg := Round(((Self.FData[(i * 8) + 0] + Self.FData[(i * 8) + 1] * 256) and $07E0) /
      $07E0 * 255);
    Color[1].RGBr := Round(((Self.FData[(i * 8) + 0] + Self.FData[(i * 8) + 1] * 256) and $F800) /
      $F800 * 255);
    Color[1].RGBa := 255;
    Color[2].RGBb := Round(((Self.FData[(i * 8) + 2] + Self.FData[(i * 8) + 3] * 256) and $001F) /
      $001F * 255);
    Color[2].RGBg := Round(((Self.FData[(i * 8) + 2] + Self.FData[(i * 8) + 3] * 256) and $07E0) /
      $07E0 * 255);
    Color[2].RGBr := Round(((Self.FData[(i * 8) + 2] + Self.FData[(i * 8) + 3] * 256) and $F800) /
      $F800 * 255);
    Color[2].RGBa := 255;
    Color[3].RGBb := Round(Color[1].RGBb / 3 * 2 + Color[2].RGBb / 3);
    Color[3].RGBg := Round(Color[1].RGBg / 3 * 2 + Color[2].RGBg / 3);
    Color[3].RGBr := Round(Color[1].RGBr / 3 * 2 + Color[2].RGBr / 3);
    Color[3].RGBa := 255;
    Color[4].RGBb := Round(Color[1].RGBb / 3 + Color[2].RGBb / 3 * 2);
    Color[4].RGBg := Round(Color[1].RGBg / 3 + Color[2].RGBg / 3 * 2);
    Color[4].RGBr := Round(Color[1].RGBr / 3 + Color[2].RGBr / 3 * 2);
    Color[4].RGBa := 255;
    Pixel[1]      := Round((Self.FData[(i * 8) + 4] and $C0) / $40 + 1);
    Pixel[2]      := Round((Self.FData[(i * 8) + 4] and $30) / $10 + 1);
    Pixel[3]      := Round((Self.FData[(i * 8) + 4] and $0C) / $04 + 1);
    Pixel[4]      := Round((Self.FData[(i * 8) + 4] and $03) + 1);
    Pixel[5]      := Round((Self.FData[(i * 8) + 5] and $C0) / $40 + 1);
    Pixel[6]      := Round((Self.FData[(i * 8) + 5] and $30) / $10 + 1);
    Pixel[7]      := Round((Self.FData[(i * 8) + 5] and $0C) / $04 + 1);
    Pixel[8]      := Round((Self.FData[(i * 8) + 5] and $03) + 1);
    Pixel[9]      := Round((Self.FData[(i * 8) + 6] and $C0) / $40 + 1);
    Pixel[10]     := Round((Self.FData[(i * 8) + 6] and $30) / $10 + 1);
    Pixel[11]     := Round((Self.FData[(i * 8) + 6] and $0C) / $04 + 1);
    Pixel[12]     := Round((Self.FData[(i * 8) + 6] and $03) + 1);
    Pixel[13]     := Round((Self.FData[(i * 8) + 7] and $C0) / $40 + 1);
    Pixel[14]     := Round((Self.FData[(i * 8) + 7] and $30) / $10 + 1);
    Pixel[15]     := Round((Self.FData[(i * 8) + 7] and $0C) / $04 + 1);
    Pixel[16]     := Round((Self.FData[(i * 8) + 7] and $03) + 1);
    for j := 0 to 3 do
    begin
      tempd[((y + 3) * Self.FWidth + x + j) * 4 + 0] := Color[Pixel[16 - j]].RGBb;
      tempd[((y + 3) * Self.FWidth + x + j) * 4 + 1] := Color[Pixel[16 - j]].RGBg;
      tempd[((y + 3) * Self.FWidth + x + j) * 4 + 2] := Color[Pixel[16 - j]].RGBr;
      tempd[((y + 3) * Self.FWidth + x + j) * 4 + 3] := 0;
    end;
    for j := 0 to 3 do
    begin
      tempd[((y + 2) * Self.FWidth + x + j) * 4 + 0] := Color[Pixel[12 - j]].RGBb;
      tempd[((y + 2) * Self.FWidth + x + j) * 4 + 1] := Color[Pixel[12 - j]].RGBg;
      tempd[((y + 2) * Self.FWidth + x + j) * 4 + 2] := Color[Pixel[12 - j]].RGBr;
      tempd[((y + 2) * Self.FWidth + x + j) * 4 + 3] := 0;
    end;
    for j := 0 to 3 do
    begin
      tempd[((y + 1) * Self.FWidth + x + j) * 4 + 0] := Color[Pixel[8 - j]].RGBb;
      tempd[((y + 1) * Self.FWidth + x + j) * 4 + 1] := Color[Pixel[8 - j]].RGBg;
      tempd[((y + 1) * Self.FWidth + x + j) * 4 + 2] := Color[Pixel[8 - j]].RGBr;
      tempd[((y + 1) * Self.FWidth + x + j) * 4 + 3] := 0;
    end;
    for j := 0 to 3 do
    begin
      tempd[((y + 0) * Self.FWidth + x + j) * 4 + 0] := Color[Pixel[4 - j]].RGBb;
      tempd[((y + 0) * Self.FWidth + x + j) * 4 + 1] := Color[Pixel[4 - j]].RGBg;
      tempd[((y + 0) * Self.FWidth + x + j) * 4 + 2] := Color[Pixel[4 - j]].RGBr;
      tempd[((y + 0) * Self.FWidth + x + j) * 4 + 3] := 0;
    end;
    x := x + 4;
    if x = Self.FWidth then
    begin
      y := y + 4;
      x := 0;
    end;
  end;
  SetLength(Self.FData, Length(tempd));
  for i := 0 to High(tempd) do
    Self.FData[i] := tempd[i];
  Self.FStoreType := 8;
  Self.FDepth    := 32;
  Self.FDataType := Self.FDataType - [DT_Oni] + [DT_Decoded32];
end;





function TOniImage.Load(ConnectionID, FileID: Integer): Boolean;
var
  FileInfo: TFileInfo;
begin
  FileInfo := ConManager.Connection[ConnectionID].GetFileInfo(fileid);
  if FileInfo.Extension = 'PSpc' then
    Result := LoadFromPSpc(ConnectionID, fileid)
  else if FileInfo.Extension = 'TXMB' then
    Result := LoadFromTXMB(ConnectionID, fileid)
  else if FileInfo.Extension = 'TXMP' then
    Result := LoadFromTXMP(ConnectionID, fileid)
  else
    Result := False;
end;




function TOniImage.LoadFromPSpc(ConnectionID, FileID: Integer): Boolean;
type
  TPoint = packed record
    X, Y: Word;
  end;

  TPSpc = packed record
    p1:   array[0..8] of TPoint;
    p2:   array[0..8] of TPoint;
    TXMP: Integer;
  end;

  TPart = packed record
    x_txmp, y_txmp: Word;
    x_pspc, y_pspc: Word;
    w, h:    Word;
    imgdata: TByteData;
    used:    Boolean;
  end;
const
  PartMatch: array[0..8] of Byte = (0, 3, 6, 1, 4, 7, 2, 5, 8);
var
  x, y, pixel: Word;
  i: Integer;

  PSpc:     TPSpc;
  txmpimg:  TOniImage;
  txmpdata: TByteData;

  parts:    array[0..8] of TPart;
  part:     Byte;
  cols:     array[0..2] of Word;
  rows:     array[0..2] of Word;
  col, row: Byte;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $08, SizeOf(PSpc), @PSpc);
  PSpc.TXMP := PSpc.TXMP div 256;
  if PSpc.TXMP = 0 then
  begin
    Result := False;
    Exit;
  end;
  txmpimg := TOniImage.Create;
  txmpimg.LoadFromTXMP(ConnectionID, PSpc.TXMP);
  txmpimg.DecodeImageTo32bit;
//  txmpimg.WriteToBMP('C:\file.bmp');
  txmpimg.GetAs32bit(txmpdata);
{    ShowMessage(IntToStr(txmpimg.Width)+'x'+IntToStr(txmpimg.Height));
    for i:=0 to High(txmpdata) do
      txmpimg.Data[i]:=txmpdata[i];
    txmpimg.WriteToBMP('D:\file2.bmp');
}
  with PSpc do
  begin
    for i := 0 to 2 do
    begin
      cols[i] := 0;
      rows[i] := 0;
    end;
    for i := 0 to 8 do
    begin
      part := PartMatch[i];
      col  := i div 3;
      row  := i mod 3;
      if (p2[i].X > 0) or (p2[i].Y > 0) then
      begin
        parts[part].x_txmp := p1[i].X - 1;
        parts[part].y_txmp := p1[i].Y - 1;
        parts[part].x_pspc := 0;
        if col > 0 then
          for x := 0 to col - 1 do
            Inc(parts[part].x_pspc, cols[x]);
        parts[part].y_pspc := 0;
        if row > 0 then
          for y := 0 to row - 1 do
            Inc(parts[part].y_pspc, rows[y]);
        parts[part].w := p2[i].X - p1[i].X + 1;
        parts[part].h := p2[i].Y - p1[i].Y + 1;
        parts[part].used := True;
        cols[col] := parts[part].w;
        rows[row] := parts[part].h;
        SetLength(parts[part].imgdata, parts[part].w * parts[part].h * 4);
        for y := 0 to parts[part].h - 1 do
        begin
          for x := 0 to parts[part].w - 1 do
          begin
            for pixel := 0 to 3 do
            begin
              parts[part].imgdata[(y * parts[part].w + x) * 4 + pixel] :=
                txmpdata[((parts[part].y_txmp + y) * txmpimg.Width +
                parts[part].x_txmp + x) * 4 + pixel];
            end;
          end;
        end;
      end
      else
      begin
        parts[part].used := False;
      end;
    end;

  end;

  txmpimg.Free;
  txmpimg := TOniImage.Create;
  for i := 0 to 8 do
  begin
    if parts[i].used then
    begin
      SetLength(txmpimg.FData, Length(parts[i].imgdata));
      for pixel := 0 to High(parts[i].imgdata) do
        txmpimg.Data[pixel] := parts[i].imgdata[pixel];
      txmpimg.Width := parts[i].w;
      txmpimg.Height    := parts[i].h;
      txmpimg.StoreType := 8;
      txmpimg.DataType  := [DT_Decoded32];
      txmpimg.Depth     := 32;
      txmpimg.WriteToBMP('M:\' + IntToStr(i) + '.bmp');
    end;
  end;
  txmpimg.Free;

  Self.FWidth  := 0;
  Self.FHeight := 0;
  for i := 0 to 2 do
  begin
    Inc(Self.FWidth, cols[i]);
    Inc(Self.FHeight, rows[i]);
  end;
  SetLength(Self.FData, Self.FWidth * Self.FHeight * 4);

  //Combine data parts

  Self.FDepth     := 32;
  Self.FStoreType := 8;
  Self.FDataType  := [DT_Decoded32];
  //    Self.RevertImage;
end;




function TOniImage.LoadFromTXMP(ConnectionID, FileID: Integer): Boolean;
var
  img_addr: Integer;
  data: TMemoryStream;
  hdr: TDDSDXTHeader;
  imginfo: Integer;
  x,y, i: Integer;
begin
  Result := True;
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $8C, SizeOf(Self.FWidth), @Self.FWidth);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $8E, SizeOf(Self.FHeight), @Self.FHeight);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $90, SizeOf(Self.FStoreType), @Self.FStoreType);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $88, SizeOf(imginfo), @imginfo);
  if ConManager.Connection[ConnectionID].DataOS = DOS_WIN then
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $9C, SizeOf(img_addr), @img_addr)
  else
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $A0, SizeOf(img_addr), @img_addr);

  case Self.FStoreType of
    0, 1, 2:
      Self.FDepth := 16;
    8:
      Self.FDepth := 32;
    9:
      Self.FDepth := 16;
    else
      Result := False;
      Exit;
  end;

  if ConManager.Connection[ConnectionID].DataOS = DOS_WIN then
    ConManager.Connection[ConnectionID].LoadRawFile(fileid, $9C, TStream(data))
  else
    ConManager.Connection[ConnectionID].LoadRawFile(fileid, $A0, TStream(data));

  with hdr do
  begin
    FOURCC := 'DDS ';
    with SURFACEDESC2 do
    begin
      Size := 124;
      Flags := DDSD_CAPS or DDSD_PIXELFORMAT or DDSD_WIDTH or DDSD_HEIGHT;
      if FStoreType = 9 then
        Flags := Flags or DDSD_LINEARSIZE
      else
        Flags := Flags or DDSD_PITCH;
      if (imginfo and $01) > 0 then
        Flags := Flags or DDSD_MIPMAPCOUNT;
      Height := FHeight;
      Width := FWidth;
      if FStoreType = 9 then
        PitchOrLinearSize := FWidth * FHeight div 2
      else
        PitchOrLinearSize := FWidth * FDepth div 2;
      Depth := 0;
      MipMapCount := 1;
      x := FWidth;
      y := FHeight;
      while (x > 1) and (y > 1) do
      begin
        x := x div 2;
        y := y div 2;
        Inc(MipMapCount);
      end;
      for i := 1 to 11 do
        Reserved[i] := 0;
      with PIXELFORMAT do
      begin
        Size := 32;
        if FStoreType = 9 then
          Flags := DDPF_FOURCC
        else
          Flags := DDPF_RGB;
      end;
    end;
  end; 
  LoadImageFromStream(data, FImage);
{
  if ConManager.Connection[ConnectionID].DataOS = DOS_WIN then
    ConManager.Connection[ConnectionID].LoadRawFile(fileid, $9C, FData)
  else
    ConManager.Connection[ConnectionID].LoadRawFile(fileid, $A0, FData);
}
  Self.FDataType := [DT_OniReverted, DT_Oni];
end;




function TOniImage.LoadFromTXMB(ConnectionID, FileID: Integer): Boolean;
var
  i, x, y, x2, y2, pixelid, imgid: Integer;
  rows, cols: Word;
  linkcount: Integer;
  link: Integer;
  images_decoded: array of TOniImage;
  x_start, y_start: Integer;
begin
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $10, SizeOf(Self.FWidth), @Self.FWidth);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $12, SizeOf(Self.FHeight), @Self.FHeight);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $18, SizeOf(cols), @cols);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1A, SizeOf(rows), @rows);
  ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $1C, SizeOf(linkcount), @linkcount);
  SetLength(images_decoded, linkcount);
  for i := 0 to linkcount - 1 do
  begin
    ConManager.Connection[ConnectionID].LoadDatFilePart(fileid, $20 + i * 4, SizeOf(link), @link);
    link := link div 256;
    images_decoded[i] := TOniImage.Create;
    images_decoded[i].LoadFromTXMP(ConnectionID, link);
    images_decoded[i].DecodeImageTo32bit;
    images_decoded[i].RevertImage;
  end;
  SetLength(Self.FData, Self.FWidth * Self.FHeight * 4);
  for y := 0 to rows - 1 do
  begin
    for x := 0 to cols - 1 do
    begin
      imgid   := y * cols + x;
      x_start := 0;
      y_start := 0;
      for i := 0 to x do
        if i < x then
          x_start := x_start + images_decoded[i].Width;
      for i := 0 to y do
        if i < y then
          y_start := y_start + images_decoded[i].Height;
      for y2 := 0 to images_decoded[imgid].Height - 1 do
      begin
        for x2 := 0 to images_decoded[imgid].Width - 1 do
        begin
          if ((x_start + x2) < Self.FWidth) and ((y_start + y2) < Self.FHeight) then
          begin
            pixelid := y_start * Self.FWidth + x_start + y2 * Self.FWidth + x2;
            Self.FData[pixelid * 4 + 0] :=
              images_decoded[imgid].Data[(y2 * images_decoded[imgid].Width + x2) * 4 + 0];
            Self.FData[pixelid * 4 + 1] :=
              images_decoded[imgid].Data[(y2 * images_decoded[imgid].Width + x2) * 4 + 1];
            Self.FData[pixelid * 4 + 2] :=
              images_decoded[imgid].Data[(y2 * images_decoded[imgid].Width + x2) * 4 + 2];
            Self.FData[pixelid * 4 + 3] :=
              images_decoded[imgid].Data[(y2 * images_decoded[imgid].Width + x2) * 4 + 3];
          end;
        end;
      end;
    end;
  end;
  for i := 0 to linkcount - 1 do
    images_decoded[i].Free;
  Self.FDepth     := 32;
  Self.FStoreType := 8;
  Self.FDataType  := [DT_Decoded32];
  Self.RevertImage;
end;



function TOniImage.GetImgSize(w,h, storetype: Integer): Integer;
begin
  case storetype of
    0, 1, 2:
      Result := w*h*2;
    8:
      Result := w*h*4;
    9:
      Result := Max(1, w div 4) * Max(1, h div 4) * 8;
  else
    Result := -1;
  end;
end;


function TOniImage.GetImageDataSize(fading: Boolean): Integer;
var
  size: Integer;
  x, y: Word;
begin
  x    := Self.FWidth;
  y    := Self.FHeight;
  size := GetImgSize(x, y, FStoreType);
  if fading then
  begin
    repeat
      x    := Max(x div 2, 1);
      y    := Max(y div 2, 1);
      size := size + GetImgSize(x, y, FStoreType);
    until (x = 1) and (y = 1);
  end;
  Result := size;
end;




procedure TOniImage.GetAsData(var Target: TStream);
var
  revert: Boolean;
begin
  //    if not (DT_Decoded32 in Self.FDataType) then
  //      Self.DecodeImage;
  if not (DT_OniReverted in Self.FDataType) then
  begin
    revert := True;
    Self.RevertImage;
  end
  else
    revert := False;
  if not Assigned(Target) then
    Target := TMemoryStream.Create;
  Target.Write(FData[0], Length(FData));
  Target.Seek(0, soFromBeginning);
  if revert then
    Self.RevertImage;
end;

procedure TOniImage.GetAsData(var Target: TByteData);
var
  mem: TStream;
begin
  mem := TMemoryStream.Create;
  GetAsData(mem);
  SetLength(Target, mem.Size);
  mem.Read(Target[0], mem.Size);
  mem.Free;
end;


procedure TOniImage.GetAs32bit(var Target: TStream);
begin
  if not (DT_Decoded32 in Self.FDataType) then
    Self.DecodeImageTo32bit;
  if not Assigned(Target) then
    Target := TMemoryStream.Create;
  Target.Write(FData[0], Length(FData));
  Target.Seek(0, soFromBeginning);
end;

procedure TOniImage.GetAs32bit(var Target: TByteData);
var
  mem: TStream;
begin
  mem := TMemoryStream.Create;
  GetAs32bit(mem);
  SetLength(Target, mem.Size);
  mem.Read(Target[0], mem.Size);
  mem.Free;
end;


procedure TOniImage.GetAsBMP(var Target: TByteData);
const
  BMPheader: array[0..53] of Byte =
    ($42, $4D, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0,
    40, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, $18, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
var
  i, x, y: Integer;
begin
  if not (DT_Decoded32 in Self.FDataType) then
    Self.DecodeImageTo32bit;

  SetLength(Target, Self.FWidth * Self.FHeight * 3 + 54);
  for y := 0 to Self.FHeight - 1 do
  begin
    for x := 0 to Self.FWidth - 1 do
    begin
      Target[((Self.FWidth * y + x) * 3) + 0 + 54] := Self.FData[(Self.FWidth * y + x) * 4 + 0];
      Target[((Self.FWidth * y + x) * 3) + 1 + 54] := Self.FData[(Self.FWidth * y + x) * 4 + 1];
      Target[((Self.FWidth * y + x) * 3) + 2 + 54] := Self.FData[(Self.FWidth * y + x) * 4 + 2];
    end;
  end;

  for i := 0 to High(BMPheader) do
    Target[i] := BMPheader[i];
  Target[2] := ((Self.FWidth * Self.FHeight * 3 + 54) and $000000FF) div $1;
  Target[3]  := ((Self.FWidth * Self.FHeight * 3 + 54) and $0000FF00) div $100;
  Target[4]  := ((Self.FWidth * Self.FHeight * 3 + 54) and $00FF0000) div $10000;
  Target[5]  := ((Self.FWidth * Self.FHeight * 3 + 54) and $FF000000) div $1000000;
  Target[18] := (Self.FWidth and $000000FF) div $1;
  Target[19] := (Self.FWidth and $0000FF00) div $100;
  Target[20] := (Self.FWidth and $00FF0000) div $10000;
  Target[21] := (Self.FWidth and $FF000000) div $1000000;
  Target[22] := (Self.FHeight and $000000FF) div $1;
  Target[23] := (Self.FHeight and $0000FF00) div $100;
  Target[24] := (Self.FHeight and $00FF0000) div $10000;
  Target[25] := (Self.FHeight and $FF000000) div $1000000;
  Target[34] := ((Self.FWidth * Self.FHeight * 3) and $000000FF) div $1;
  Target[35] := ((Self.FWidth * Self.FHeight * 3) and $0000FF00) div $100;
  Target[36] := ((Self.FWidth * Self.FHeight * 3) and $00FF0000) div $10000;
  Target[37] := ((Self.FWidth * Self.FHeight * 3) and $FF000000) div $1000000;
end;


procedure TOniImage.GetAsBMP(var Target: TStream);
var
  data: TByteData;
  streampos: Integer;
begin
  GetAsBMP(data);
  streampos := Target.Position;
  Target.Write(data[0], Length(data));
  Target.Seek(streampos, soFromBeginning);
end;


function TOniImage.LoadFromBMP(filename: String): Boolean;
var
  filestream: TFileStream;
  tempd:      TByteData;

  x, y: Integer;
begin
  filestream := TFileStream.Create(filename, fmOpenRead);
  SetLength(tempd, filestream.Size);
  filestream.Read(tempd[0], filestream.Size);
  filestream.Free;

  if not ((tempd[00] = $42) and (tempd[01] = $4D)) then
  begin
    Result := False;
    ShowMessage('Not a standard 24bit bitmap');
    Exit;
  end;
  if not (tempd[10] = 54) then
  begin
    Result := False;
    ShowMessage('Imagedata has to start at 0x54');
    Exit;
  end;
  if not (tempd[14] = 40) then
  begin
    Result := False;
    ShowMessage('Second bitmap header has to have 40 bytes');
    Exit;
  end;
  if not (tempd[28] = 24) then
  begin
    Result := False;
    ShowMessage('Bitmap has to have 24bits');
    Exit;
  end;
  if not (tempd[30] = 0) then
  begin
    Result := False;
    ShowMessage('Bitmap has to be uncompressed');
    Exit;
  end;

  Self.FWidth     := tempd[18] + tempd[19] * 256 + tempd[20] * 256 * 256 + tempd[21] * 256 * 256 * 256;
  Self.FHeight    := tempd[22] + tempd[23] * 256 + tempd[24] * 256 * 256 + tempd[25] * 256 * 256 * 256;
  Self.FDepth     := 32;
  Self.FStoreType := 8;

  SetLength(Self.FData, Self.FWidth * Self.FHeight * Self.FDepth div 8);
  for y := 0 to Self.FHeight - 1 do
  begin
    for x := 0 to Self.FWidth - 1 do
    begin
      Self.FData[((Self.FWidth * y + x) * 4) + 0] := tempd[54 + (Self.FWidth * y + x) * 3 + 0];
      Self.FData[((Self.FWidth * y + x) * 4) + 1] := tempd[54 + (Self.FWidth * y + x) * 3 + 1];
      Self.FData[((Self.FWidth * y + x) * 4) + 2] := tempd[54 + (Self.FWidth * y + x) * 3 + 2];
      Self.FData[((Self.FWidth * y + x) * 4) + 3] := 0;
    end;
  end;

  Self.FDataType := [DT_Decoded32];
end;




function TOniImage.WriteToBMP(filename: String): Boolean;
var
  filestream: TFileStream;
begin
  filestream := TFileStream.Create(filename, fmCreate);
  GetAsBMP(TStream(filestream));
  filestream.Free;
end;



function TOniImage.GetMipMappedImage(var Target: TByteData): Boolean;
var
  i:      Integer;
  x, y:   Word;
  fadelvldata: TByteData;
  revert: Boolean;
begin
  Result := False;

  //    if not (DT_Decoded32 in Self.FDataType) then
  //      Self.DecodeImage;
  if Self.FStoreType = 9 then
    Self.DecompressImage;
  if not (DT_OniReverted in Self.FDataType) then
  begin
    revert := True;
    Self.RevertImage;
  end
  else
    revert := False;

  x := Self.FWidth;
  y := Self.FHeight;
  SetLength(Target, x * y * Self.FDepth div 8);
  SetLength(fadelvldata, x * y * Self.FDepth div 8);
  for i := 0 to Length(Target) - 1 do
  begin
    Target[i] := Self.FData[i];
    fadelvldata[i] := Self.FData[i];
  end;
  repeat
    fadelvldata := Self.ResizeImage(x, y, fadelvldata);
    x := x div 2;
    y := y div 2;
    SetLength(Target, Length(Target) + x * y * Self.FDepth div 8);
    for i := 0 to Length(fadelvldata) - 1 do
      Target[Length(Target) - x * y * Self.FDepth div 8 + i] := fadelvldata[i];
  until (x = 1) or (y = 1) or ((x mod 2) = 1) or ((y mod 2) = 1);
  if (x > 1) and (y > 1) then
    Exit;
  Result := True;

  if revert then
    Self.RevertImage;
end;


function TOniImage.GetMipMappedImage(var Target: TStream): Boolean;
var
  data: TByteData;
  streampos: Integer;
begin
  Result := GetMipMappedImage(data);
  if not Assigned(Target) then
    Target := TMemoryStream.Create;
  streampos := Target.Position;
  Target.Write(data[0], Length(data));
  Target.Seek(streampos, soFromBeginning);
end;


end.