0,0 → 1,1304 |
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<FWidth) and (y>=0) and (y<FHeight) then |
begin |
case FBitCount of |
1 : begin |
if FPackedPixelOrder then |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 3)^ and Mask1[7-x and 7]) shr Shift1[7-x and 7] |
else |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 3)^ and Mask1[x and 7]) shr Shift1[x and 7]; |
end; |
2 : begin |
if FPackedPixelOrder then |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 2)^ and Mask2[3-x and 3]) shr Shift2[3-x and 3] |
else |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 2)^ and Mask2[x and 3]) shr Shift2[x and 3]; |
end; |
4 : begin |
if FPackedPixelOrder then |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 1)^ and Mask4[1-x and 1]) shr Shift4[1-x and 1] |
else |
Result := (PByte(Integer(FTopPBits)+FNextLine*y+x shr 1)^ and Mask4[x and 1]) shr Shift4[x and 1]; |
end; |
8 : Result := PByte(Integer(FTopPBits)+FNextLine*y+x)^; |
16: Result := PWord(Integer(FTopPBits)+FNextLine*y+x*2)^; |
24: PByte3(@Result)^ := PByte3(Integer(FTopPBits)+FNextLine*y+x*3)^; |
32: Result := PDWORD(Integer(FTopPBits)+FNextLine*y+x*4)^; |
end; |
end; |
end; |
|
procedure TDXTextureImage.SetPixel(x, y: Integer; c: DWORD); |
var |
P: PByte; |
begin |
if (x>=0) and (x<FWidth) and (y>=0) and (y<FHeight) then |
begin |
case FBitCount of |
1 : begin |
P := Pointer(Integer(FTopPBits)+FNextLine*y+x shr 3); |
if FPackedPixelOrder then |
P^ := (P^ and (not Mask1[7-x and 7])) or ((c and 1) shl Shift1[7-x and 7]) |
else |
P^ := (P^ and (not Mask1[x and 7])) or ((c and 1) shl Shift1[x and 7]); |
end; |
2 : begin |
P := Pointer(Integer(FTopPBits)+FNextLine*y+x shr 2); |
if FPackedPixelOrder then |
P^ := (P^ and (not Mask2[3-x and 3])) or ((c and 3) shl Shift2[3-x and 3]) |
else |
P^ := (P^ and (not Mask2[x and 3])) or ((c and 3) shl Shift2[x and 3]); |
end; |
4 : begin |
P := Pointer(Integer(FTopPBits)+FNextLine*y+x shr 1); |
if FPackedPixelOrder then |
P^ := (P^ and (not Mask4[1-x and 1])) or ((c and 7) shl Shift4[1-x and 1]) |
else |
P^ := (P^ and (not Mask4[x and 1])) or ((c and 7) shl Shift4[x and 1]); |
end; |
8 : PByte(Integer(FTopPBits)+FNextLine*y+x)^ := c; |
16: PWord(Integer(FTopPBits)+FNextLine*y+x*2)^ := c; |
24: PByte3(Integer(FTopPBits)+FNextLine*y+x*3)^ := PByte3(@c)^; |
32: PDWORD(Integer(FTopPBits)+FNextLine*y+x*4)^ := c; |
end; |
end; |
end; |
|
procedure TDXTextureImage.LoadFromFile(const FileName: string); |
var |
Stream: TFileStream; |
begin |
Stream := TFileStream.Create(FileName, fmOpenRead or fmShareDenyWrite); |
try |
LoadFromStream(Stream); |
finally |
Stream.Free; |
end; |
end; |
|
procedure TDXTextureImage.LoadFromStream(Stream: TStream); |
var |
i, p: Integer; |
begin |
Clear; |
|
p := Stream.Position; |
for i:=0 to DXTextureImageLoadFuncList.Count-1 do |
begin |
Stream.Position := p; |
try |
TDXTextureImageLoadFunc(DXTextureImageLoadFuncList[i])(Stream, Self); |
Exit; |
except |
Clear; |
end; |
end; |
|
raise EDXTextureImageError.Create(SNotSupportGraphicFile); |
end; |
|
procedure TDXTextureImage.SaveToFile(const FileName: string); |
var |
Stream: TFileStream; |
begin |
Stream := TFileStream.Create(FileName, fmCreate); |
try |
SaveToStream(Stream); |
finally |
Stream.Free; |
end; |
end; |
|
procedure DXTextureImage_SaveDXTextureImageFunc(Stream: TStream; Image: TDXTextureImage); forward; |
|
procedure TDXTextureImage.SaveToStream(Stream: TStream); |
begin |
DXTextureImage_SaveDXTextureImageFunc(Stream, Self); |
end; |
|
{ DXTextureImage_LoadDXTextureImageFunc } |
|
const |
DXTextureImageFile_Type = 'dxt:'; |
DXTextureImageFile_Version = $100; |
|
DXTextureImageCompress_None = 0; |
|
DXTextureImageFileCategoryType_Image = $100; |
|
DXTextureImageFileBlockID_EndFile = 0; |
DXTextureImageFileBlockID_EndGroup = 1; |
DXTextureImageFileBlockID_StartGroup = 2; |
DXTextureImageFileBlockID_Image_Format = DXTextureImageFileCategoryType_Image + 1; |
DXTextureImageFileBlockID_Image_PixelData = DXTextureImageFileCategoryType_Image + 2; |
DXTextureImageFileBlockID_Image_GroupInfo = DXTextureImageFileCategoryType_Image + 3; |
DXTextureImageFileBlockID_Image_Name = DXTextureImageFileCategoryType_Image + 4; |
DXTextureImageFileBlockID_Image_TransparentColor = DXTextureImageFileCategoryType_Image + 5; |
|
type |
TDXTextureImageFileHeader = packed record |
FileType: array[0..4] of Char; |
ver: DWORD; |
end; |
|
TDXTextureImageFileBlockHeader = packed record |
ID: DWORD; |
Size: Integer; |
end; |
|
TDXTextureImageFileBlockHeader_StartGroup = packed record |
CategoryType: DWORD; |
end; |
|
TDXTextureImageHeader_Image_Format = packed record |
ImageType: TDXTextureImageType; |
Width: DWORD; |
Height: DWORD; |
BitCount: DWORD; |
WidthBytes: DWORD; |
end; |
|
TDXTextureImageHeader_Image_Format_Index = packed record |
idx_index_Mask: DWORD; |
idx_alpha_Mask: DWORD; |
idx_palette: array[0..255] of TPaletteEntry; |
end; |
|
TDXTextureImageHeader_Image_Format_RGB = packed record |
rgb_red_Mask: DWORD; |
rgb_green_Mask: DWORD; |
rgb_blue_Mask: DWORD; |
rgb_alpha_Mask: DWORD; |
end; |
|
TDXTextureImageHeader_Image_GroupInfo = packed record |
ImageGroupType: DWORD; |
ImageID: DWORD; |
end; |
|
TDXTextureImageHeader_Image_TransparentColor = packed record |
Transparent: Boolean; |
TransparentColor: DWORD; |
end; |
|
procedure DXTextureImage_LoadDXTextureImageFunc(Stream: TStream; Image: TDXTextureImage); |
|
procedure ReadGroup_Image(Image: TDXTextureImage); |
var |
i: Integer; |
BlockHeader: TDXTextureImageFileBlockHeader; |
NextPos: Integer; |
SubImage: TDXTextureImage; |
Header_StartGroup: TDXTextureImageFileBlockHeader_StartGroup; |
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; |
ImageName: string; |
begin |
while True do |
begin |
Stream.ReadBuffer(BlockHeader, SizeOf(BlockHeader)); |
NextPos := Stream.Position + BlockHeader.Size; |
|
case BlockHeader.ID of |
DXTextureImageFileBlockID_EndGroup: |
begin |
{ End of group } |
Break; |
end; |
DXTextureImageFileBlockID_StartGroup: |
begin |
{ Beginning of group } |
Stream.ReadBuffer(Header_StartGroup, SizeOf(Header_StartGroup)); |
case Header_StartGroup.CategoryType of |
DXTextureImageFileCategoryType_Image: |
begin |
{ Image group } |
SubImage := TDXTextureImage.CreateSub(Image); |
try |
ReadGroup_Image(SubImage); |
except |
SubImage.Free; |
raise; |
end; |
end; |
end; |
end; |
DXTextureImageFileBlockID_Image_Format: |
begin |
{ Image information reading (size etc.) } |
Stream.ReadBuffer(Header_Image_Format, SizeOf(Header_Image_Format)); |
|
if (Header_Image_Format.ImageType<>DXTextureImageType_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 j<BI.biBitCount then |
Image.rgb_alpha := dxtMakeChannel((1 shl (BI.biBitCount-j)-1) shl j, False); |
|
Image.PackedPixelOrder := False; |
end; |
|
{ palette reading } |
PalCount := BI.biClrUsed; |
if (PalCount=0) and (BI.biBitCount<=8) then |
PalCount := 1 shl BI.biBitCount; |
if PalCount>256 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. |