www.pudn.com > DelphiX_for7.zip > DXTexImg.pas


unit DXTexImg; 
 
interface 
 
uses 
  Windows, SysUtils, Classes, DXConsts; 
 
const 
  DXTextureImageGroupType_Normal = 0; // Normal group 
  DXTextureImageGroupType_Mipmap = 1; // Mipmap group 
 
type 
  EDXTextureImageError = class(Exception); 
 
  TDXTextureImageChannel = record 
    Mask: DWORD; 
    BitCount: Integer; 
 
    {  Internal use  } 
    _Mask2: DWORD; 
    _rshift: Integer; 
    _lshift: Integer; 
    _BitCount2: Integer; 
  end; 
 
  TDXTextureImage_PaletteEntries =  array[0..255] of TPaletteEntry; 
 
  TDXTextureImageType = ( 
    DXTextureImageType_PaletteIndexedColor, 
    DXTextureImageType_RGBColor 
  ); 
 
  TDXTextureImage = class; 
 
  TDXTextureImageLoadFunc = procedure(Stream: TStream; Image: TDXTextureImage); 
 
  TDXTextureImage = class 
  private 
    FOwner: TDXTextureImage; 
    FSubImage: TList; 
    FImageType: TDXTextureImageType; 
    FWidth: Integer; 
    FHeight: Integer; 
    FPBits: Pointer; 
    FBitCount: Integer; 
    FPackedPixelOrder: Boolean; 
    FWidthBytes: Integer; 
    FNextLine: Integer; 
    FSize: Integer; 
    FTopPBits: Pointer; 
    FTransparent: Boolean; 
    FTransparentColor: DWORD; 
    FImageGroupType: DWORD; 
    FImageID: DWORD; 
    FImageName: string; 
    FAutoFreeImage: Boolean; 
    procedure ClearImage; 
    function GetPixel(x, y: Integer): DWORD; 
    procedure SetPixel(x, y: Integer; c: DWORD); 
    function GetScanLine(y: Integer): Pointer; 
    function GetSubGroupImageCount(GroupTypeID: DWORD): Integer; 
    function GetSubGroupImage(GroupTypeID: DWORD; Index: Integer): TDXTextureImage; 
    function GetSubImageCount: Integer; 
    function GetSubImage(Index: Integer): TDXTextureImage; 
  public 
    idx_index: TDXTextureImageChannel; 
    idx_alpha: TDXTextureImageChannel; 
    idx_palette: TDXTextureImage_PaletteEntries; 
    rgb_red: TDXTextureImageChannel; 
    rgb_green: TDXTextureImageChannel; 
    rgb_blue: TDXTextureImageChannel; 
    rgb_alpha: TDXTextureImageChannel; 
    constructor Create; 
    constructor CreateSub(AOwner: TDXTextureImage); 
    destructor Destroy; override; 
    procedure Assign(Source: TDXTextureImage); 
    procedure Clear; 
    procedure SetImage(ImageType: TDXTextureImageType; Width, Height, BitCount, WidthBytes, NextLine: Integer; 
      PBits, TopPBits: Pointer; Size: Integer; AutoFree: Boolean); 
    procedure SetSize(ImageType: TDXTextureImageType; Width, Height, BitCount, WidthBytes: Integer); 
    procedure LoadFromFile(const FileName: string); 
    procedure LoadFromStream(Stream: TStream); 
    procedure SaveToFile(const FileName: string); 
    procedure SaveToStream(Stream: TStream); 
    function EncodeColor(R, G, B, A: Byte): DWORD; 
    function PaletteIndex(R, G, B: Byte): DWORD; 
    class procedure RegisterLoadFunc(LoadFunc: TDXTextureImageLoadFunc); 
    class procedure UnRegisterLoadFunc(LoadFunc: TDXTextureImageLoadFunc); 
    property BitCount: Integer read FBitCount; 
    property PackedPixelOrder: Boolean read FPackedPixelOrder write FPackedPixelOrder; 
    property Height: Integer read FHeight; 
    property ImageType: TDXTextureImageType read FImageType; 
    property ImageGroupType: DWORD read FImageGroupType write FImageGroupType; 
    property ImageID: DWORD read FImageID write FImageID; 
    property ImageName: string read FImageName write FImageName; 
    property NextLine: Integer read FNextLine; 
    property PBits: Pointer read FPBits; 
    property Pixels[x, y: Integer]: DWORD read GetPixel write SetPixel; 
    property ScanLine[y: Integer]: Pointer read GetScanLine; 
    property Size: Integer read FSize; 
    property SubGroupImageCount[GroupTypeID: DWORD]: Integer read GetSubGroupImageCount; 
    property SubGroupImages[GroupTypeID: DWORD; Index: Integer]: TDXTextureImage read GetSubGroupImage; 
    property SubImageCount: Integer read GetSubImageCount; 
    property SubImages[Index: Integer]: TDXTextureImage read GetSubImage; 
    property TopPBits: Pointer read FTopPBits; 
    property Transparent: Boolean read FTransparent write FTransparent; 
    property TransparentColor: DWORD read FTransparentColor write FTransparentColor; 
    property Width: Integer read FWidth; 
    property WidthBytes: Integer read FWidthBytes; 
  end; 
 
function dxtMakeChannel(Mask: DWORD; indexed: Boolean): TDXTextureImageChannel; 
function dxtEncodeChannel(const Channel: TDXTextureImageChannel; c: DWORD): DWORD; 
function dxtDecodeChannel(const Channel: TDXTextureImageChannel; c: DWORD): DWORD; 
 
implementation 
 
function GetWidthBytes(Width, BitCount: Integer): Integer; 
begin 
  Result := (((Width*BitCount)+31) div 32)*4; 
end; 
 
function dxtEncodeChannel(const Channel: TDXTextureImageChannel; c: DWORD): DWORD; 
begin 
  Result := ((c shl Channel._rshift) shr Channel._lshift) and Channel.Mask; 
end; 
 
function dxtDecodeChannel(const Channel: TDXTextureImageChannel; c: DWORD): DWORD; 
begin 
  Result := ((c  and Channel.Mask) shr Channel._rshift) shl Channel._lshift; 
  Result := Result or (Result shr Channel._BitCount2); 
end; 
 
function dxtMakeChannel(Mask: DWORD; indexed: Boolean): TDXTextureImageChannel; 
 
  function GetMaskBitCount(b: Integer): Integer; 
  var 
    i: Integer; 
  begin 
    i := 0; 
    while (i<31) and (((1 shl i) and b)=0) do Inc(i); 
 
    Result := 0; 
    while ((1 shl i) and b)<>0 do 
    begin 
      Inc(i); 
      Inc(Result); 
    end; 
  end; 
 
  function GetBitCount2(b: Integer): Integer; 
  begin 
    Result := 0; 
    while (Result<31) and (((1 shl Result) and b)=0) do Inc(Result); 
  end; 
 
begin 
  Result.BitCount := GetMaskBitCount(Mask); 
  Result.Mask := Mask; 
 
  if indexed then 
  begin 
    Result._rshift := GetBitCount2(Mask); 
    Result._lshift := 0; 
    Result._Mask2 := 1 shl Result.BitCount-1; 
    Result._BitCount2 := 0; 
  end else 
  begin 
    Result._rshift := GetBitCount2(Mask)-(8-Result.BitCount); 
    if Result._rshift<0 then 
    begin 
      Result._lshift := -Result._rshift; 
      Result._rshift := 0; 
    end else 
      Result._lshift := 0; 
    Result._Mask2 := (1 shl Result.BitCount-1) shl (8-Result.BitCount); 
    Result._BitCount2 := 8-Result.BitCount; 
  end; 
end; 
 
{  TDXTextureImage  } 
 
var 
  _DXTextureImageLoadFuncList: TList; 
 
procedure DXTextureImage_LoadDXTextureImageFunc(Stream: TStream; Image: TDXTextureImage); forward; 
procedure DXTextureImage_LoadBitmapFunc(Stream: TStream; Image: TDXTextureImage); forward; 
 
function DXTextureImageLoadFuncList: TList; 
begin 
  if _DXTextureImageLoadFuncList=nil then 
  begin 
    _DXTextureImageLoadFuncList := TList.Create; 
    _DXTextureImageLoadFuncList.Add(@DXTextureImage_LoadDXTextureImageFunc); 
    _DXTextureImageLoadFuncList.Add(@DXTextureImage_LoadBitmapFunc); 
  end; 
  Result := _DXTextureImageLoadFuncList; 
end; 
 
class procedure TDXTextureImage.RegisterLoadFunc(LoadFunc: TDXTextureImageLoadFunc); 
begin 
  if DXTextureImageLoadFuncList.IndexOf(@LoadFunc)=-1 then 
    DXTextureImageLoadFuncList.Add(@LoadFunc); 
end; 
 
class procedure TDXTextureImage.UnRegisterLoadFunc(LoadFunc: TDXTextureImageLoadFunc); 
begin 
  DXTextureImageLoadFuncList.Remove(@LoadFunc); 
end; 
 
constructor TDXTextureImage.Create; 
begin 
  inherited Create; 
  FSubImage := TList.Create; 
end; 
 
constructor TDXTextureImage.CreateSub(AOwner: TDXTextureImage); 
begin 
  Create; 
 
  FOwner := AOwner; 
  try            
    FOwner.FSubImage.Add(Self); 
  except 
    FOwner := nil; 
    raise; 
  end; 
end; 
 
destructor TDXTextureImage.Destroy; 
begin 
  Clear; 
  FSubImage.Free; 
  if FOwner<>nil then 
    FOwner.FSubImage.Remove(Self); 
  inherited Destroy; 
end; 
 
procedure TDXTextureImage.Assign(Source: TDXTextureImage); 
var 
  y: Integer; 
begin 
  SetSize(Source.ImageType, Source.Width, Source.Height, Source.BitCount, Source.WidthBytes); 
 
  idx_index := Source.idx_index; 
  idx_alpha := Source.idx_alpha; 
  idx_palette := Source.idx_palette; 
 
  rgb_red := Source.rgb_red; 
  rgb_green := Source.rgb_green; 
  rgb_blue := Source.rgb_blue; 
  rgb_alpha := Source.rgb_alpha; 
 
  for y:=0 to Height-1 do 
    Move(Source.ScanLine[y]^, ScanLine[y]^, WidthBytes); 
 
  Transparent := Source.Transparent; 
  TransparentColor := Source.TransparentColor; 
  ImageGroupType := Source.ImageGroupType; 
  ImageID := Source.ImageID; 
  ImageName := Source.ImageName; 
end; 
 
procedure TDXTextureImage.ClearImage; 
begin 
  if FAutoFreeImage then 
    FreeMem(FPBits); 
 
  FImageType := DXTextureImageType_PaletteIndexedColor; 
  FWidth := 0; 
  FHeight := 0; 
  FBitCount := 0; 
  FWidthBytes := 0; 
  FNextLine := 0; 
  FSize := 0; 
  FPBits := nil; 
  FTopPBits := nil; 
  FAutoFreeImage := False; 
end; 
 
procedure TDXTextureImage.Clear; 
begin 
  ClearImage; 
 
  while SubImageCount>0 do 
    SubImages[SubImageCount-1].Free; 
 
  FImageGroupType := 0; 
  FImageID := 0; 
  FImageName := ''; 
 
  FTransparent := False; 
  FTransparentColor := 0; 
 
  FillChar(idx_index, SizeOf(idx_index), 0); 
  FillChar(idx_alpha, SizeOf(idx_alpha), 0); 
  FillChar(idx_palette, SizeOf(idx_palette), 0); 
  FillChar(rgb_red, SizeOf(rgb_red), 0); 
  FillChar(rgb_green, SizeOf(rgb_green), 0); 
  FillChar(rgb_blue, SizeOf(rgb_blue), 0); 
  FillChar(rgb_alpha, SizeOf(rgb_alpha), 0); 
end; 
 
procedure TDXTextureImage.SetImage(ImageType: TDXTextureImageType; Width, Height, BitCount, WidthBytes, NextLine: Integer; 
  PBits, TopPBits: Pointer; Size: Integer; AutoFree: Boolean); 
begin 
  ClearImage; 
 
  FAutoFreeImage := AutoFree; 
  FImageType := ImageType; 
  FWidth := Width; 
  FHeight := Height; 
  FBitCount := BitCount; 
  FWidthBytes := WidthBytes; 
  FNextLine := NextLine; 
  FSize := Size; 
  FPBits := PBits; 
  FTopPBits := TopPBits; 
end; 
 
procedure TDXTextureImage.SetSize(ImageType: TDXTextureImageType; Width, Height, BitCount, WidthBytes: Integer); 
var 
  APBits: Pointer; 
begin 
  ClearImage; 
 
  if WidthBytes=0 then 
    WidthBytes := GetWidthBytes(Width, BitCount); 
 
  GetMem(APBits, WidthBytes*Height); 
  SetImage(ImageType, Width, Height, BitCount, WidthBytes, WidthBytes, APBits, APBits, WidthBytes*Height, True); 
end; 
 
function TDXTextureImage.GetScanLine(y: Integer): Pointer; 
begin 
  Result := Pointer(Integer(FTopPBits)+FNextLine*y); 
end; 
 
function TDXTextureImage.GetSubGroupImageCount(GroupTypeID: DWORD): Integer; 
var 
  i: Integer; 
begin 
  Result := 0; 
  for i:=0 to SubImageCount-1 do 
    if SubImages[i].ImageGroupType=GroupTypeID then 
      Inc(Result); 
end; 
 
function TDXTextureImage.GetSubGroupImage(GroupTypeID: DWORD; Index: Integer): TDXTextureImage; 
var 
  i, j: Integer; 
begin 
  j := 0; 
  for i:=0 to SubImageCount-1 do 
    if SubImages[i].ImageGroupType=GroupTypeID then 
    begin 
      if j=Index then 
      begin 
        Result := SubImages[i]; 
        Exit; 
      end; 
 
      Inc(j); 
    end; 
 
  Result := nil; 
  SubImages[-1]; 
end; 
 
function TDXTextureImage.GetSubImageCount: Integer; 
begin 
  Result := FSubImage.Count; 
end; 
 
function TDXTextureImage.GetSubImage(Index: Integer): TDXTextureImage; 
begin 
  Result := FSubImage[Index]; 
end; 
 
function TDXTextureImage.EncodeColor(R, G, B, A: Byte): DWORD; 
begin 
  if ImageType=DXTextureImageType_PaletteIndexedColor then 
  begin 
    Result := dxtEncodeChannel(idx_index, PaletteIndex(R, G, B)) or 
      dxtEncodeChannel(idx_alpha, A); 
  end else 
  begin 
    Result := dxtEncodeChannel(rgb_red, R) or 
      dxtEncodeChannel(rgb_green, G) or 
      dxtEncodeChannel(rgb_blue, B) or 
      dxtEncodeChannel(rgb_alpha, A); 
 end; 
end; 
 
function TDXTextureImage.PaletteIndex(R, G, B: Byte): DWORD; 
var 
  i, d, d2: Integer; 
begin 
  Result := 0; 
  if ImageType=DXTextureImageType_PaletteIndexedColor then 
  begin 
    d := MaxInt; 
    for i:=0 to (1 shl idx_index.BitCount)-1 do 
      with idx_palette[i] do 
      begin 
        d2 := Abs((peRed-R))*Abs((peRed-R)) + Abs((peGreen-G))*Abs((peGreen-G)) + Abs((peBlue-B))*Abs((peBlue-B)); 
        if d>d2 then 
        begin 
          d := d2; 
          Result := i; 
        end; 
      end; 
  end; 
end; 
 
const 
  Mask1: array[0..7] of DWORD= (1, 2, 4, 8, 16, 32, 64, 128); 
  Mask2: array[0..3] of DWORD= (3, 12, 48, 192); 
  Mask4: array[0..1] of DWORD= ($0F, $F0); 
 
  Shift1: array[0..7] of DWORD= (0, 1, 2, 3, 4, 5, 6, 7); 
  Shift2: array[0..3] of DWORD= (0, 2, 4, 6); 
  Shift4: array[0..1] of DWORD= (0, 4); 
 
type 
  PByte3 = ^TByte3; 
  TByte3 = array[0..2] of Byte; 
 
function TDXTextureImage.GetPixel(x, y: Integer): DWORD; 
begin 
  Result := 0; 
  if (x>=0) and (x=0) and (y=0) and (x=0) and (yDXTextureImageType_PaletteIndexedColor) and 
              (Header_Image_Format.ImageType<>DXTextureImageType_RGBColor) then 
              raise EDXTextureImageError.Create(SInvalidDXTFile); 
 
            Image.SetSize(Header_Image_Format.ImageType, Header_Image_Format.Width, Header_Image_Format.Height, 
              Header_Image_Format.BitCount, Header_Image_Format.Widthbytes); 
 
            if Header_Image_Format.ImageType=DXTextureImageType_PaletteIndexedColor then 
            begin 
              {  INDEX IMAGE  } 
              Stream.ReadBuffer(Header_Image_Format_Index, SizeOf(Header_Image_Format_Index)); 
 
              Image.idx_index := dxtMakeChannel(Header_Image_Format_Index.idx_index_Mask, True); 
              Image.idx_alpha := dxtMakeChannel(Header_Image_Format_Index.idx_alpha_Mask, False); 
 
              for i:=0 to 255 do 
                Image.idx_palette[i] := Header_Image_Format_Index.idx_palette[i]; 
            end else if Header_Image_Format.ImageType=DXTextureImageType_RGBColor then 
            begin 
              {  RGB IMAGE  } 
              Stream.ReadBuffer(Header_Image_Format_RGB, SizeOf(Header_Image_Format_RGB)); 
 
              Image.rgb_red := dxtMakeChannel(Header_Image_Format_RGB.rgb_red_Mask, False); 
              Image.rgb_green := dxtMakeChannel(Header_Image_Format_RGB.rgb_green_Mask, False); 
              Image.rgb_blue := dxtMakeChannel(Header_Image_Format_RGB.rgb_blue_Mask, False); 
              Image.rgb_alpha := dxtMakeChannel(Header_Image_Format_RGB.rgb_alpha_Mask, False); 
            end; 
          end; 
        DXTextureImageFileBlockID_Image_Name: 
          begin 
            {  Name reading  } 
            SetLength(ImageName, BlockHeader.Size); 
            Stream.ReadBuffer(ImageName[1], BlockHeader.Size); 
 
            Image.ImageName := ImageName; 
          end; 
        DXTextureImageFileBlockID_Image_GroupInfo: 
          begin 
            {  Image group information reading  } 
            Stream.ReadBuffer(Header_Image_GroupInfo, SizeOf(Header_Image_GroupInfo)); 
 
            Image.ImageGroupType := Header_Image_GroupInfo.ImageGroupType; 
            Image.ImageID := Header_Image_GroupInfo.ImageID; 
          end; 
        DXTextureImageFileBlockID_Image_TransparentColor: 
          begin 
            {  Transparent color information reading  } 
            Stream.ReadBuffer(Header_Image_TransparentColor, SizeOf(Header_Image_TransparentColor)); 
 
            Image.Transparent := Header_Image_TransparentColor.Transparent; 
            Image.TransparentColor := Header_Image_TransparentColor.TransparentColor; 
          end; 
        DXTextureImageFileBlockID_Image_PixelData: 
          begin 
            {  Pixel data reading  } 
            for i:=0 to Image.Height-1 do 
              Stream.ReadBuffer(Image.ScanLine[i]^, Header_Image_Format.Widthbytes); 
          end; 
      end; 
 
      Stream.Seek(NextPos, soFromBeginning); 
    end; 
  end; 
 
var 
  FileHeader: TDXTextureImageFileHeader; 
  BlockHeader: TDXTextureImageFileBlockHeader; 
  Header_StartGroup: TDXTextureImageFileBlockHeader_StartGroup; 
  NextPos: Integer; 
begin 
  {  File header reading  } 
  Stream.ReadBuffer(FileHeader, SizeOf(FileHeader)); 
 
  if FileHeader.FileType<>DXTextureImageFile_Type then 
    raise EDXTextureImageError.Create(SInvalidDXTFile); 
  if FileHeader.ver<>DXTextureImageFile_Version then 
    raise EDXTextureImageError.Create(SInvalidDXTFile); 
 
  while True do 
  begin 
    Stream.ReadBuffer(BlockHeader, SizeOf(BlockHeader)); 
    NextPos := Stream.Position + BlockHeader.Size; 
 
    case BlockHeader.ID of 
      DXTextureImageFileBlockID_EndFile: 
        begin 
          {  End of file  } 
          Break; 
        end; 
      DXTextureImageFileBlockID_StartGroup: 
        begin 
          {  Beginning of group  } 
          Stream.ReadBuffer(Header_StartGroup, SizeOf(Header_StartGroup)); 
          case Header_StartGroup.CategoryType of 
            DXTextureImageFileCategoryType_Image: ReadGroup_Image(Image); 
          end; 
        end; 
    end; 
     
    Stream.Seek(NextPos, soFromBeginning); 
  end; 
end; 
 
type 
  PDXTextureImageFileBlockHeaderWriter_BlockInfo = ^TDXTextureImageFileBlockHeaderWriter_BlockInfo; 
  TDXTextureImageFileBlockHeaderWriter_BlockInfo = record 
    BlockID: DWORD; 
    StreamPos: Integer; 
  end; 
 
  TDXTextureImageFileBlockHeaderWriter = class 
  private 
    FStream: TStream; 
    FList: TList; 
  public 
    constructor Create(Stream: TStream); 
    destructor Destroy; override; 
    procedure StartBlock(BlockID: DWORD); 
    procedure EndBlock; 
    procedure WriteBlock(BlockID: DWORD); 
    procedure StartGroup(CategoryType: DWORD); 
    procedure EndGroup; 
  end; 
 
constructor TDXTextureImageFileBlockHeaderWriter.Create(Stream: TStream); 
begin 
  inherited Create; 
  FStream := Stream; 
  FList := TList.Create; 
end; 
 
destructor TDXTextureImageFileBlockHeaderWriter.Destroy; 
var 
  i: Integer; 
begin 
  for i:=0 to FList.Count-1 do 
    Dispose(PDXTextureImageFileBlockHeaderWriter_BlockInfo(FList[i])); 
  FList.Free; 
  inherited Destroy; 
end; 
 
procedure TDXTextureImageFileBlockHeaderWriter.StartBlock(BlockID: DWORD); 
var 
  BlockInfo: PDXTextureImageFileBlockHeaderWriter_BlockInfo; 
  BlockHeader: TDXTextureImageFileBlockHeader; 
begin 
  New(BlockInfo); 
  BlockInfo.BlockID := BlockID; 
  BlockInfo.StreamPos := FStream.Position; 
  FList.Add(BlockInfo); 
 
  BlockHeader.ID := BlockID; 
  BlockHeader.Size := 0; 
  FStream.WriteBuffer(BlockHeader, SizeOf(BlockHeader)); 
end; 
 
procedure TDXTextureImageFileBlockHeaderWriter.EndBlock; 
var 
  BlockHeader: TDXTextureImageFileBlockHeader; 
  BlockInfo: PDXTextureImageFileBlockHeaderWriter_BlockInfo; 
  CurStreamPos: Integer; 
begin 
  CurStreamPos := FStream.Position; 
  try 
    BlockInfo := FList[FList.Count-1]; 
 
    FStream.Position := BlockInfo.StreamPos; 
    BlockHeader.ID := BlockInfo.BlockID; 
    BlockHeader.Size := CurStreamPos-(BlockInfo.StreamPos+SizeOf(TDXTextureImageFileBlockHeader)); 
    FStream.WriteBuffer(BlockHeader, SizeOf(BlockHeader)); 
  finally 
    FStream.Position := CurStreamPos; 
 
    Dispose(FList[FList.Count-1]); 
    FList.Count := FList.Count-1; 
  end; 
end; 
 
procedure TDXTextureImageFileBlockHeaderWriter.WriteBlock(BlockID: DWORD); 
var 
  BlockHeader: TDXTextureImageFileBlockHeader; 
begin 
  BlockHeader.ID := BlockID; 
  BlockHeader.Size := 0; 
  FStream.WriteBuffer(BlockHeader, SizeOf(BlockHeader)); 
end; 
 
procedure TDXTextureImageFileBlockHeaderWriter.StartGroup(CategoryType: DWORD); 
var 
  Header_StartGroup: TDXTextureImageFileBlockHeader_StartGroup; 
begin 
  StartBlock(DXTextureImageFileBlockID_StartGroup); 
 
  Header_StartGroup.CategoryType := CategoryType; 
  FStream.WriteBuffer(Header_StartGroup, SizeOf(Header_StartGroup)); 
end; 
 
procedure TDXTextureImageFileBlockHeaderWriter.EndGroup; 
begin 
  WriteBlock(DXTextureImageFileBlockID_EndGroup); 
  EndBlock; 
end; 
 
procedure DXTextureImage_SaveDXTextureImageFunc(Stream: TStream; Image: TDXTextureImage); 
var 
  BlockHeaderWriter: TDXTextureImageFileBlockHeaderWriter; 
 
  function CalcProgressCount(Image: TDXTextureImage): Integer; 
  var 
    i: Integer; 
  begin 
    Result := Image.WidthBytes*Image.Height; 
    for i:=0 to Image.SubImageCount-1 do 
      Inc(Result, CalcProgressCount(Image.SubImages[i])); 
  end; 
 
  procedure WriteGroup_Image(Image: TDXTextureImage); 
  var 
    i: Integer; 
    Header_Image_Format: TDXTextureImageHeader_Image_Format; 
    Header_Image_Format_Index: TDXTextureImageHeader_Image_Format_Index; 
    Header_Image_Format_RGB: TDXTextureImageHeader_Image_Format_RGB; 
    Header_Image_GroupInfo: TDXTextureImageHeader_Image_GroupInfo; 
    Header_Image_TransparentColor: TDXTextureImageHeader_Image_TransparentColor; 
  begin 
    BlockHeaderWriter.StartGroup(DXTextureImageFileCategoryType_Image); 
    try 
      {  Image format writing  } 
      if Image.Size>0 then 
      begin 
        Header_Image_Format.ImageType := Image.ImageType; 
        Header_Image_Format.Width := Image.Width; 
        Header_Image_Format.Height := Image.Height; 
        Header_Image_Format.BitCount := Image.BitCount; 
        Header_Image_Format.WidthBytes := Image.WidthBytes; 
 
        BlockHeaderWriter.StartBlock(DXTextureImageFileBlockID_Image_Format); 
        try 
          Stream.WriteBuffer(Header_Image_Format, SizeOf(Header_Image_Format)); 
 
          case Image.ImageType of 
            DXTextureImageType_PaletteIndexedColor: 
              begin 
                {  INDEX IMAGE  } 
                Header_Image_Format_Index.idx_index_Mask := Image.idx_index.Mask; 
                Header_Image_Format_Index.idx_alpha_Mask := Image.idx_alpha.Mask; 
                for i:=0 to 255 do 
                  Header_Image_Format_Index.idx_palette[i] := Image.idx_palette[i]; 
 
                Stream.WriteBuffer(Header_Image_Format_Index, SizeOf(Header_Image_Format_Index)); 
              end; 
            DXTextureImageType_RGBColor: 
              begin 
                {  RGB IMAGE  } 
                Header_Image_Format_RGB.rgb_red_Mask := Image.rgb_red.Mask; 
                Header_Image_Format_RGB.rgb_green_Mask := Image.rgb_green.Mask; 
                Header_Image_Format_RGB.rgb_blue_Mask := Image.rgb_blue.Mask; 
                Header_Image_Format_RGB.rgb_alpha_Mask := Image.rgb_alpha.Mask; 
 
                Stream.WriteBuffer(Header_Image_Format_RGB, SizeOf(Header_Image_Format_RGB)); 
              end; 
          end; 
        finally 
          BlockHeaderWriter.EndBlock; 
        end; 
      end; 
 
      {  Image group information writing  } 
      BlockHeaderWriter.StartBlock(DXTextureImageFileBlockID_Image_GroupInfo); 
      try 
        Header_Image_GroupInfo.ImageGroupType := Image.ImageGroupType; 
        Header_Image_GroupInfo.ImageID := Image.ImageID; 
 
        Stream.WriteBuffer(Header_Image_GroupInfo, SizeOf(Header_Image_GroupInfo)); 
      finally 
        BlockHeaderWriter.EndBlock; 
      end; 
 
      {  Name writing  } 
      BlockHeaderWriter.StartBlock(DXTextureImageFileBlockID_Image_Name); 
      try 
        Stream.WriteBuffer(Image.ImageName[1], Length(Image.ImageName)); 
      finally 
        BlockHeaderWriter.EndBlock; 
      end; 
 
      {  Transparent color writing  } 
      BlockHeaderWriter.StartBlock(DXTextureImageFileBlockID_Image_TransparentColor); 
      try 
        Header_Image_TransparentColor.Transparent := Image.Transparent; 
        Header_Image_TransparentColor.TransparentColor := Image.TransparentColor; 
 
        Stream.WriteBuffer(Header_Image_TransparentColor, SizeOf(Header_Image_TransparentColor)); 
      finally 
        BlockHeaderWriter.EndBlock; 
      end; 
 
      {  Pixel data writing  } 
      if Image.Size>0 then 
      begin 
        BlockHeaderWriter.StartBlock(DXTextureImageFileBlockID_Image_PixelData); 
        try 
         for i:=0 to Image.Height-1 do 
           Stream.WriteBuffer(Image.ScanLine[i]^, Image.Widthbytes); 
        finally 
          BlockHeaderWriter.EndBlock; 
        end; 
      end; 
 
      {  Sub-image writing  } 
      for i:=0 to Image.SubImageCount-1 do 
        WriteGroup_Image(Image.SubImages[i]); 
    finally 
      BlockHeaderWriter.EndGroup; 
    end; 
  end; 
 
var 
  FileHeader: TDXTextureImageFileHeader; 
begin 
  {  File header writing  } 
  FileHeader.FileType := DXTextureImageFile_Type; 
  FileHeader.ver := DXTextureImageFile_Version; 
  Stream.WriteBuffer(FileHeader, SizeOf(FileHeader)); 
 
  {  Image writing  } 
  BlockHeaderWriter := TDXTextureImageFileBlockHeaderWriter.Create(Stream); 
  try 
    {  Image writing  } 
    WriteGroup_Image(Image); 
 
    {  End of file  } 
    BlockHeaderWriter.WriteBlock(DXTextureImageFileBlockID_EndFile); 
  finally 
    BlockHeaderWriter.Free; 
  end; 
end; 
 
{  DXTextureImage_LoadBitmapFunc  } 
 
procedure DXTextureImage_LoadBitmapFunc(Stream: TStream; Image: TDXTextureImage); 
type 
  TDIBPixelFormat = packed record 
    RBitMask, GBitMask, BBitMask: DWORD; 
  end; 
var 
  TopDown: Boolean; 
  BF: TBitmapFileHeader; 
  BI: TBitmapInfoHeader; 
 
  procedure DecodeRGB; 
  var 
    y: Integer; 
  begin 
    for y:=0 to Image.Height-1 do 
    begin 
      if TopDown then 
        Stream.ReadBuffer(Image.ScanLine[y]^, Image.WidthBytes) 
      else 
        Stream.ReadBuffer(Image.ScanLine[Image.Height-y-1]^, Image.WidthBytes); 
    end; 
  end; 
 
  procedure DecodeRLE4; 
  var 
    SrcDataP: Pointer; 
    B1, B2, C: Byte; 
    Dest, Src, P: PByte; 
    X, Y, i: Integer; 
  begin 
    GetMem(SrcDataP, BI.biSizeImage); 
    try 
      Stream.ReadBuffer(SrcDataP^, BI.biSizeImage); 
 
      Dest := Image.TopPBits; 
      Src := SrcDataP; 
      X := 0; 
      Y := 0; 
 
      while True do 
      begin 
        B1 := Src^; Inc(Src); 
        B2 := Src^; Inc(Src); 
 
        if B1=0 then 
        begin 
          case B2 of 
            0: begin  {  End of line  } 
                 X := 0; Inc(Y); 
                 Dest := Image.ScanLine[Y]; 
               end; 
            1: Break; {  End of bitmap  } 
            2: begin  {  Difference of coordinates  } 
                 Inc(X, B1); Inc(Y, B2); Inc(Src, 2); 
                 Dest := Image.ScanLine[Y]; 
               end; 
          else 
            {  Absolute mode  } 
            C := 0; 
            for i:=0 to B2-1 do 
            begin 
              if i and 1=0 then 
              begin 
                C := Src^; Inc(Src); 
              end else 
              begin 
                C := C shl 4; 
              end; 
 
              P := Pointer(Integer(Dest)+X shr 1); 
              if X and 1=0 then 
                P^ := (P^ and $0F) or (C and $F0) 
              else 
                P^ := (P^ and $F0) or ((C and $F0) shr 4); 
 
              Inc(X); 
            end; 
          end; 
        end else 
        begin 
          {  Encoding mode  } 
          for i:=0 to B1-1 do 
          begin 
            P := Pointer(Integer(Dest)+X shr 1); 
            if X and 1=0 then 
              P^ := (P^ and $0F) or (B2 and $F0) 
            else 
              P^ := (P^ and $F0) or ((B2 and $F0) shr 4); 
 
            Inc(X); 
 
            // Swap nibble 
            B2 := (B2 shr 4) or (B2 shl 4); 
          end; 
        end; 
 
        {  Word arrangement  } 
        Inc(Src, Longint(Src) and 1); 
      end; 
    finally 
      FreeMem(SrcDataP); 
    end; 
  end; 
 
  procedure DecodeRLE8; 
  var 
    SrcDataP: Pointer; 
    B1, B2: Byte; 
    Dest, Src: PByte; 
    X, Y: Integer; 
  begin 
    GetMem(SrcDataP, BI.biSizeImage); 
    try 
      Stream.ReadBuffer(SrcDataP^, BI.biSizeImage); 
 
      Dest := Image.TopPBits; 
      Src := SrcDataP; 
      X := 0; 
      Y := 0; 
 
      while True do 
      begin 
        B1 := Src^; Inc(Src); 
        B2 := Src^; Inc(Src); 
 
        if B1=0 then 
        begin 
          case B2 of 
            0: begin  {  End of line  } 
                 X := 0; Inc(Y); 
                 Dest := Pointer(Longint(Image.TopPBits)+Y*Image.NextLine+X); 
               end; 
            1: Break; {  End of bitmap  } 
            2: begin  {  Difference of coordinates  } 
                 Inc(X, B1); Inc(Y, B2); Inc(Src, 2); 
                 Dest := Pointer(Longint(Image.TopPBits)+Y*Image.NextLine+X); 
               end; 
          else 
            {  Absolute mode  } 
            Move(Src^, Dest^, B2); Inc(Dest, B2); Inc(Src, B2); 
          end; 
        end else 
        begin 
          {  Encoding mode  } 
          FillChar(Dest^, B1, B2); Inc(Dest, B1); 
        end; 
 
        {  Word arrangement  } 
        Inc(Src, Longint(Src) and 1); 
      end; 
    finally 
      FreeMem(SrcDataP); 
    end; 
  end; 
 
var 
  BC: TBitmapCoreHeader; 
  RGBTriples: array[0..255] of TRGBTriple; 
  RGBQuads: array[0..255] of TRGBQuad; 
  i, PalCount, j: Integer; 
  OS2: Boolean; 
  PixelFormat: TDIBPixelFormat; 
begin 
  {  File header reading  } 
  i := Stream.Read(BF, SizeOf(TBitmapFileHeader)); 
  if i=0 then Exit; 
  if i<>SizeOf(TBitmapFileHeader) then 
    raise EDXTextureImageError.Create(SInvalidDIB); 
 
  {  Is the head 'BM'?  } 
  if BF.bfType<>Ord('B') + Ord('M')*$100 then 
    raise EDXTextureImageError.Create(SInvalidDIB); 
 
  {  Reading of size of header  } 
  i := Stream.Read(BI.biSize, 4); 
  if i<>4 then 
    raise EDXTextureImageError.Create(SInvalidDIB); 
 
  {  Kind check of DIB  } 
  OS2 := False; 
 
  case BI.biSize of 
    SizeOf(TBitmapCoreHeader): 
      begin 
        {  OS/2 type  } 
        Stream.ReadBuffer(Pointer(Integer(@BC)+4)^, SizeOf(TBitmapCoreHeader)-4); 
 
        FilLChar(BI, SizeOf(BI), 0); 
        with BI do 
        begin 
          biClrUsed := 0; 
          biCompression := BI_RGB; 
          biBitCount := BC.bcBitCount; 
          biHeight := BC.bcHeight; 
          biWidth := BC.bcWidth; 
        end; 
 
        OS2 := True; 
      end; 
    SizeOf(TBitmapInfoHeader): 
      begin 
        {  Windows type  } 
        Stream.ReadBuffer(Pointer(Integer(@BI)+4)^, SizeOf(TBitmapInfoHeader)-4); 
      end; 
  else 
    raise EDXTextureImageError.Create(SInvalidDIB); 
  end; 
 
  {  Bit mask reading  } 
  if BI.biCompression = BI_BITFIELDS then 
  begin 
    Stream.ReadBuffer(PixelFormat, SizeOf(PixelFormat)); 
  end else 
  begin 
    if BI.biBitCount=16 then 
    begin 
      PixelFormat.RBitMask := $7C00; 
      PixelFormat.GBitMask := $03E0; 
      PixelFormat.BBitMask := $001F; 
    end else if (BI.biBitCount=24) or (BI.biBitCount=32) then 
    begin 
      PixelFormat.RBitMask := $00FF0000; 
      PixelFormat.GBitMask := $0300FF00; 
      PixelFormat.BBitMask := $000000FF; 
    end; 
  end; 
 
  {  DIB making  } 
  if BI.biHeight<0 then 
  begin 
    BI.biHeight := -BI.biHeight; 
    TopDown := True; 
  end else 
    TopDown := False; 
 
  if BI.biBitCount in [1, 4, 8] then 
  begin 
    Image.SetSize(DXTextureImageType_PaletteIndexedColor, BI.biWidth, BI.biHeight, BI.biBitCount, 
      (((BI.biWidth*BI.biBitCount)+31) div 32)*4); 
 
    Image.idx_index := dxtMakeChannel(1 shl BI.biBitCount-1, True); 
    Image.PackedPixelOrder := True; 
  end else 
  begin           
    Image.SetSize(DXTextureImageType_RGBColor, BI.biWidth, BI.biHeight, BI.biBitCount, 
      (((BI.biWidth*BI.biBitCount)+31) div 32)*4); 
 
    Image.rgb_red := dxtMakeChannel(PixelFormat.RBitMask, False); 
    Image.rgb_green := dxtMakeChannel(PixelFormat.GBitMask, False); 
    Image.rgb_blue := dxtMakeChannel(PixelFormat.BBitMask, False); 
 
    j := Image.rgb_red.BitCount+Image.rgb_green.BitCount+Image.rgb_blue.BitCount; 
    if j256 then PalCount := 256; 
 
  if OS2 then 
  begin 
    {  OS/2 type  } 
    Stream.ReadBuffer(RGBTriples, SizeOf(TRGBTriple)*PalCount); 
    for i:=0 to PalCount-1 do 
    begin 
      Image.idx_palette[i].peRed := RGBTriples[i].rgbtRed; 
      Image.idx_palette[i].peGreen := RGBTriples[i].rgbtGreen; 
      Image.idx_palette[i].peBlue := RGBTriples[i].rgbtBlue; 
    end; 
  end else 
  begin 
    {  Windows type  } 
    Stream.ReadBuffer(RGBQuads, SizeOf(TRGBQuad)*PalCount); 
    for i:=0 to PalCount-1 do 
    begin 
      Image.idx_palette[i].peRed := RGBQuads[i].rgbRed; 
      Image.idx_palette[i].peGreen := RGBQuads[i].rgbGreen; 
      Image.idx_palette[i].peBlue := RGBQuads[i].rgbBlue; 
    end; 
  end; 
 
  {  Pixel data reading  } 
  case BI.biCompression of 
    BI_RGB      : DecodeRGB; 
    BI_BITFIELDS: DecodeRGB; 
    BI_RLE4     : DecodeRLE4; 
    BI_RLE8     : DecodeRLE8; 
  else 
    raise EDXTextureImageError.Create(SInvalidDIB); 
  end; 
end; 
 
initialization 
finalization 
  _DXTextureImageLoadFuncList.Free; 
end.