unit OniImgClass;

interface

uses Math, Dialogs, Types, SysUtils, Classes, Data, OniDataClass;

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

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

    function ResizeImage(oldx, oldy: LongWord; img: Tdata): Tdata;
    procedure RevertImage;
    procedure DecodeImage;
    procedure DecompressImage;
  protected
  public
    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: Tdata Read FData Write FData;

    constructor Create;
    function Load(connection: TOniData; fileid: Cardinal): Boolean;
    function LoadFromPSpc(connection: TOniData; fileid: LongWord): Boolean;
    function LoadFromTXMP(connection: TOniData; fileid: LongWord): Boolean;
    function LoadFromTXMB(connection: TOniData; fileid: LongWord): Boolean;
    function GetImageDataSize(fading: Boolean): LongWord;

    function GetAsData: Tdata;
    function GetAs32bit: Tdata;
    function GetAsBMP: Tdata;
    function LoadFromBMP(filename: String): Boolean;
    function WriteToBMP(filename: String): Boolean;
    function GetMipMappedImage(var faded: Tdata): Boolean;
  published
  end;


implementation

uses Functions;




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;
end;




function TOniImage.ResizeImage(oldx, oldy: LongWord; img: Tdata): Tdata;
var
  i, j: LongWord;
  col, row, row_orig: LongWord;
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: LongWord;
  tempd:   Tdata;
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.DecodeImage;
var
  x, y:  LongWord;
  tempd: Tdata;
begin
  if not (DT_Decoded32 in Self.FDataType) then
  begin
    SetLength(tempd, Self.FWidth * Self.FHeight * 4);
    case Self.FStoreType of
      0:
      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:
      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;
      9:
      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: LongWord;
  color:      array[1..4] of Tcolor;
  pixel:      array[1..16] of Byte;
  tempd:      Tdata;
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(connection: TOniData; fileid: Cardinal): Boolean;
var
  FileInfo: TFileInfo;
  ext:      String;
begin
  FileInfo := connection.GetFileInfo(fileid);
  if FileInfo.Extension = 'PSpc' then
    Result := LoadFromPSpc(connection, fileid)
  else if FileInfo.Extension = 'TXMB' then
    Result := LoadFromTXMB(connection, fileid)
  else if FileInfo.Extension = 'TXMP' then
    Result := LoadFromTXMP(connection, fileid)
  else
    Result := False;
end;




function TOniImage.LoadFromPSpc(connection: TOniData; fileid: LongWord): 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: LongWord;
  end;

  TPart = packed record
    x_txmp, y_txmp: Word;
    x_pspc, y_pspc: Word;
    w, h:    Word;
    imgdata: Tdata;
    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: Tdata;

  parts:    array[0..8] of TPart;
  part:     Byte;
  cols:     array[0..2] of Word;
  rows:     array[0..2] of Word;
  col, row: Byte;
begin
  connection.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(connection, PSpc.TXMP);
  txmpimg.DecodeImage;
  txmpimg.WriteToBMP('C:\file.bmp');
  txmpdata := txmpimg.GetAs32bit;
{    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(connection: TOniData; fileid: LongWord): Boolean;
var
  img_addr: LongWord;
begin
  Result := True;
  connection.LoadDatFilePart(fileid, $8C, SizeOf(Self.FWidth), @Self.FWidth);
  connection.LoadDatFilePart(fileid, $8E, SizeOf(Self.FHeight), @Self.FHeight);
  connection.LoadDatFilePart(fileid, $90, SizeOf(Self.FStoreType),
    @Self.FStoreType);
  if not connection.OSisMac then
    connection.LoadDatFilePart(fileid, $9C, SizeOf(img_addr), @img_addr)
  else
    connection.LoadDatFilePart(fileid, $A0, SizeOf(img_addr), @img_addr);

  case Self.FStoreType of
    0, 1, 2:
    begin
      SetLength(Self.FData, Self.FWidth * Self.FHeight * 2);
      Self.FDepth := 16;
    end;
    8:
    begin
      SetLength(Self.FData, Self.FWidth * Self.FHeight * 4);
      Self.FDepth := 32;
    end;
    9:
    begin
      SetLength(Self.FData, Self.FWidth * Self.FHeight div 2);
      Self.FDepth := 16;
    end;
    else
      Result := False;
      Exit;
  end;

  if not connection.OSisMac then
    connection.LoadRawFile(fileid, $9C, @Self.FData[0])
  else
    connection.LoadRawFile(fileid, $A0, @Self.FData[0]);

  Self.FDataType := [DT_OniReverted, DT_Oni];
end;




function TOniImage.LoadFromTXMB(connection: TOniData; fileid: LongWord): Boolean;
var
  i, x, y, x2, y2, pixelid, imgid: LongWord;
  rows, cols: Word;
  linkcount: LongWord;
  link: LongWord;
  images_decoded: array of TOniImage;
  x_start, y_start: LongWord;
begin
  connection.LoadDatFilePart(fileid, $10, SizeOf(Self.FWidth), @Self.FWidth);
  connection.LoadDatFilePart(fileid, $12, SizeOf(Self.FHeight), @Self.FHeight);
  connection.LoadDatFilePart(fileid, $18, SizeOf(cols), @cols);
  connection.LoadDatFilePart(fileid, $1A, SizeOf(rows), @rows);
  connection.LoadDatFilePart(fileid, $1C, SizeOf(linkcount), @linkcount);
  SetLength(images_decoded, linkcount);
  for i := 0 to linkcount - 1 do
  begin
    connection.LoadDatFilePart(fileid, $20 + i * 4, SizeOf(link), @link);
    link := link div 256;
    images_decoded[i] := TOniImage.Create;
    images_decoded[i].LoadFromTXMP(connection, link);
    images_decoded[i].DecodeImage;
    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.GetImageDataSize(fading: Boolean): LongWord;
var
  size: LongWord;
  x, y: Word;
  bpp:  Byte;
begin
  case Self.FStoreType of
    9:
      bpp := 8;
    0, 1, 2:
      bpp := 16;
    8:
      bpp := 32;
    else
      Result := 0;
      Exit;
  end;

  x    := Self.FWidth;
  y    := Self.FHeight;
  size := x * y * bpp div 8;
  if fading then
  begin
    repeat
      x    := x div 2;
      y    := y div 2;
      size := size + x * y * bpp div 8;
    until (x = 1) or (y = 1);
  end;
  Result := size;
end;




function TOniImage.GetAsData: Tdata;
var
  i:      Integer;
  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;
  SetLength(Result, Length(Self.FData));
  for i := 0 to High(Result) do
    Result[i] := Self.FData[i];
  if revert then
    Self.RevertImage;
end;




function TOniImage.GetAs32bit: Tdata;
var
  i: Integer;
begin
  if not (DT_Decoded32 in Self.FDataType) then
    Self.DecodeImage;
  SetLength(Result, Length(Self.FData));
  for i := 0 to High(Result) do
    Result[i] := Self.FData[i];
end;




function TOniImage.GetAsBMP: Tdata;
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: LongWord;
begin
  if not (DT_Decoded32 in Self.FDataType) then
    Self.DecodeImage;

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

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




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

  x, y: LongWord;
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;
  tempd:      Tdata;
begin
  tempd      := Self.GetAsBMP;
  filestream := TFileStream.Create(filename, fmCreate);
  filestream.Write(tempd[0], Length(tempd));
  filestream.Free;
end;




function TOniImage.GetMipMappedImage(var faded: Tdata): Boolean;
var
  i:      LongWord;
  x, y:   Word;
  fadelvldata: Tdata;
  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(faded, x * y * Self.FDepth div 8);
  SetLength(fadelvldata, x * y * Self.FDepth div 8);
  for i := 0 to Length(faded) - 1 do
  begin
    faded[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(faded, Length(faded) + x * y * Self.FDepth div 8);
    for i := 0 to Length(fadelvldata) - 1 do
      faded[Length(faded) - 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;


end.