瀏覽代碼

* Bitfield support for LoadDDS

* refactored SaveDDS
master
Bergmann89 12 年之前
父節點
當前提交
475288680c
共有 1 個文件被更改,包括 187 次插入115 次删除
  1. +187
    -115
      glBitmap.pas

+ 187
- 115
glBitmap.pas 查看文件

@@ -591,7 +591,7 @@ type


//////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////
TglBitmapFormat = ( TglBitmapFormat = (
tfEmpty = 0,
tfEmpty = 0, //must be smallest value!


tfAlpha4, tfAlpha4,
tfAlpha8, tfAlpha8,
@@ -1609,8 +1609,6 @@ end;
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes; function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes;
begin begin
result := [ftDDS];

if (aFormat in [ if (aFormat in [
//4 bbp //4 bbp
tfLuminance4, tfLuminance4,
@@ -1646,9 +1644,29 @@ begin
tfRGB10A2, tfRGBA8, tfBGR10A2, tfBGRA8]) then tfRGB10A2, tfRGBA8, tfBGR10A2, tfBGRA8]) then
result := result + [ftTGA]; result := result + [ftTGA];


//TODO Supported File Formats!
if (aFormat in [
//8 bpp
tfAlpha8, tfLuminance8, tfLuminance4Alpha4, tfLuminance6Alpha2,
tfR3G3B2, tfRGBA2, tfBGRA2,

//16 bpp
tfAlpha16, tfLuminance16, tfLuminance8Alpha8, tfLuminance12Alpha4,
tfRGB4, tfR5G6B5, tfRGB5, tfRGBA4, tfRGB5A1,
tfBGR4, tfB5G6R5, tfBGR5, tfBGRA4, tfBGR5A1,


(*
//24 bpp
tfRGB8, tfBGR8,

//32 bbp
tfLuminance16Alpha16,
tfRGBA8, tfRGB10A2,
tfBGRA8, tfBGR10A2,

//compressed
tfS3tcDtx1RGBA, tfS3tcDtx3RGBA, tfS3tcDtx5RGBA]) then
result := result + [ftDDS];

(* TODO
{$IFDEF GLB_SUPPORT_PNG_WRITE} {$IFDEF GLB_SUPPORT_PNG_WRITE}
if aFormat in [ if aFormat in [
tfAlpha4, tfAlpha8, tfAlpha12, tfAlpha16, tfAlpha4, tfAlpha8, tfAlpha12, tfAlpha16,
@@ -6974,7 +6992,7 @@ end;
//DDS///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //DDS/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const const
DDS_MAGIC = $20534444;
DDS_MAGIC: Cardinal = $20534444;


// DDS_header.dwFlags // DDS_header.dwFlags
DDSD_CAPS = $00000001; DDSD_CAPS = $00000001;
@@ -6988,6 +7006,7 @@ const


// DDS_header.sPixelFormat.dwFlags // DDS_header.sPixelFormat.dwFlags
DDPF_ALPHAPIXELS = $00000001; DDPF_ALPHAPIXELS = $00000001;
DDPF_ALPHA = $00000002;
DDPF_FOURCC = $00000004; DDPF_FOURCC = $00000004;
DDPF_INDEXED = $00000020; DDPF_INDEXED = $00000020;
DDPF_RGB = $00000040; DDPF_RGB = $00000040;
@@ -7032,7 +7051,6 @@ type
end; end;


TDDSHeader = packed record TDDSHeader = packed record
dwMagic: Cardinal;
dwSize: Cardinal; dwSize: Cardinal;
dwFlags: Cardinal; dwFlags: Cardinal;
dwHeight: Cardinal; dwHeight: Cardinal;
@@ -7050,8 +7068,14 @@ type
function TglBitmap.LoadDDS(const aStream: TStream): Boolean; function TglBitmap.LoadDDS(const aStream: TStream): Boolean;
var var
Header: TDDSHeader; Header: TDDSHeader;
Converter: TbmpBitfieldFormat;


function GetDDSFormat: TglBitmapFormat; function GetDDSFormat: TglBitmapFormat;
var
fd: TFormatDescriptor;
i: Integer;
Range: TglBitmapColorRec;
match: Boolean;
begin begin
result := tfEmpty; result := tfEmpty;
with Header.PixelFormat do begin with Header.PixelFormat do begin
@@ -7062,66 +7086,79 @@ var
D3DFMT_DXT3: result := tfS3tcDtx3RGBA; D3DFMT_DXT3: result := tfS3tcDtx3RGBA;
D3DFMT_DXT5: result := tfS3tcDtx5RGBA; D3DFMT_DXT5: result := tfS3tcDtx5RGBA;
end; end;
end else

// RGB
if (dwFlags and (DDPF_RGB or DDPF_ALPHAPIXELS or DDPF_LUMINANCE)) > 0 then begin
case dwRGBBitCount of
8: begin
if ((dwFlags and DDPF_ALPHAPIXELS) > 0) then
result := tfAlpha8
else if ((dwFlags and DDPF_LUMINANCE) > 0) then
result := tfLuminance8;
end;

16: begin
if ((dwFlags and DDPF_ALPHAPIXELS) > 0) then begin
case CountSetBits(dwRBitMask) of
5: result := tfRGB5A1;
4: result := tfRGBA4;
else
result := tfLuminance8Alpha8;
end;
end else if (CountSetBits(dwGBitMask) = 6) then
result := tfR5G6B5
else
result := tfRGB5;
end;
end else if ((Header.PixelFormat.dwFlags and (DDPF_RGB or DDPF_ALPHAPIXELS or DDPF_LUMINANCE)) > 0) then begin

//find matching format
for result := High(TglBitmapFormat) downto Low(TglBitmapFormat) do begin
fd := TFormatDescriptor.Get(result);
if fd.MaskMatch(dwRBitMask, dwGBitMask, dwBBitMask, dwABitMask) and
(8 * fd.PixelSize = dwRGBBitCount) then
exit;
end;


24: begin
result := tfRGB8;
end;
//find format with same Range
Range.r := dwRBitMask;
Range.g := dwGBitMask;
Range.b := dwBBitMask;
Range.a := dwABitMask;
for i := 0 to 3 do begin
while ((Range.arr[i] and 1) = 0) and (Range.arr[i] > 0) do
Range.arr[i] := Range.arr[i] shr 1;
end;
for result := High(TglBitmapFormat) downto Low(TglBitmapFormat) do begin
fd := TFormatDescriptor.Get(result);
match := true;
for i := 0 to 3 do
if (fd.Range.arr[i] <> Range.arr[i]) then begin
match := false;
break;
end;
if match then
break;
end;


32: begin
if CountSetBits(dwRBitMask) = 10 then
result := tfRGB10A2
else
result := tfRGBA8;
end;
//no format with same range found -> use default
if (result = tfEmpty) then begin
if (dwABitMask > 0) then
result := tfBGRA8
else
result := tfBGR8;
end; end;


if (dwRBitMask <> 0) and (dwBBitMask <> 0) and (dwRBitMask > dwBBitMask) then
result := TFormatDescriptor.Get(result).RGBInverted;
Converter := TbmpBitfieldFormat.Create;
Converter.RedMask := dwRBitMask;
Converter.GreenMask := dwGBitMask;
Converter.BlueMask := dwBBitMask;
Converter.AlphaMask := dwABitMask;
Converter.PixelSize := dwRGBBitCount / 8;
end; end;
end; end;
end; end;


var var
StreamPos: Int64; StreamPos: Int64;
Y, LineSize: Cardinal;
RowSize: Cardinal;
NewImage, TmpData: PByte;
x, y, j, LineSize, RowSize, Magic: Cardinal;
NewImage, TmpData, RowData, SrcData: PByte;
SourceMD, DestMD: Pointer;
Pixel: TglBitmapPixelData;
ddsFormat: TglBitmapFormat; ddsFormat: TglBitmapFormat;
FormatDesc: TFormatDescriptor; FormatDesc: TFormatDescriptor;


begin begin
result := false;

// Header
result := false;
Converter := nil;
StreamPos := aStream.Position; StreamPos := aStream.Position;
aStream.Read(Header, sizeof(Header));


if (Header.dwMagic <> DDS_MAGIC) or (Header.dwSize <> 124) or
// Magic
aStream.Read(Magic, sizeof(Magic));
if (Magic <> DDS_MAGIC) then begin
aStream.Position := StreamPos;
exit;
end;

//Header
aStream.Read(Header, sizeof(Header));
if (Header.dwSize <> SizeOf(Header)) or
((Header.dwFlags and (DDSD_PIXELFORMAT or DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT)) <> ((Header.dwFlags and (DDSD_PIXELFORMAT or DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT)) <>
(DDSD_PIXELFORMAT or DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT)) then (DDSD_PIXELFORMAT or DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT)) then
begin begin
@@ -7133,39 +7170,74 @@ begin
raise EglBitmapException.Create('LoadDDS - CubeMaps are not supported'); raise EglBitmapException.Create('LoadDDS - CubeMaps are not supported');


ddsFormat := GetDDSFormat; ddsFormat := GetDDSFormat;
if (ddsFormat = tfEmpty) then
raise EglBitmapException.Create('LoadDDS - unsupported Pixelformat found.');

FormatDesc := TFormatDescriptor.Get(ddsFormat);
LineSize := Trunc(Header.dwWidth * FormatDesc.PixelSize);
GetMem(NewImage, Header.dwHeight * LineSize);
try try
TmpData := NewImage;

// Compressed
if ((Header.PixelFormat.dwFlags and DDPF_FOURCC) > 0) then begin
RowSize := Header.dwPitchOrLinearSize div Header.dwWidth;
for Y := 0 to Header.dwHeight-1 do begin
aStream.Read(TmpData^, RowSize);
Inc(TmpData, LineSize);
end;
end else

// Uncompressed
if (Header.PixelFormat.dwFlags and (DDPF_RGB or DDPF_ALPHAPIXELS or DDPF_LUMINANCE)) > 0 then begin
RowSize := (Header.PixelFormat.dwRGBBitCount * Header.dwWidth) shr 3;
for Y := 0 to Header.dwHeight-1 do begin
aStream.Read(TmpData^, RowSize);
Inc(TmpData, LineSize);
end;
end else
if (ddsFormat = tfEmpty) then
raise EglBitmapException.Create('LoadDDS - unsupported Pixelformat found.'); raise EglBitmapException.Create('LoadDDS - unsupported Pixelformat found.');


SetDataPointer(NewImage, ddsFormat, Header.dwWidth, Header.dwHeight);
result := true;
except
FreeMem(NewImage);
raise;
FormatDesc := TFormatDescriptor.Get(ddsFormat);
LineSize := Trunc(Header.dwWidth * FormatDesc.PixelSize);
GetMem(NewImage, Header.dwHeight * LineSize);
try
TmpData := NewImage;

//Converter needed
if Assigned(Converter) then begin
RowSize := Round(Header.dwWidth * Header.PixelFormat.dwRGBBitCount / 8);
GetMem(RowData, RowSize);
SourceMD := Converter.CreateMappingData;
DestMD := FormatDesc.CreateMappingData;
try
for y := 0 to Header.dwHeight-1 do begin
TmpData := NewImage + y * LineSize;
SrcData := RowData;
aStream.Read(SrcData^, RowSize);
for x := 0 to Header.dwWidth-1 do begin
Converter.Unmap(SrcData, Pixel, SourceMD);
//TODO use converter function
for j := 0 to 3 do
if (Converter.Range.arr[j] <> FormatDesc.Range.arr[j]) then begin
if (Converter.Range.arr[j] > 0) then
Pixel.Data.arr[j] := Round(Pixel.Data.arr[j] / Converter.Range.arr[j] * FormatDesc.Range.arr[j])
else
Pixel.Data.arr[j] := 0;
end;
FormatDesc.Map(Pixel, TmpData, DestMD);
end;
end;
finally
Converter.FreeMappingData(SourceMD);
FormatDesc.FreeMappingData(DestMD);
FreeMem(RowData);
end;
end else

// Compressed
if ((Header.PixelFormat.dwFlags and DDPF_FOURCC) > 0) then begin
RowSize := Header.dwPitchOrLinearSize div Header.dwWidth;
for Y := 0 to Header.dwHeight-1 do begin
aStream.Read(TmpData^, RowSize);
Inc(TmpData, LineSize);
end;
end else

// Uncompressed
if (Header.PixelFormat.dwFlags and (DDPF_RGB or DDPF_ALPHAPIXELS or DDPF_LUMINANCE)) > 0 then begin
RowSize := (Header.PixelFormat.dwRGBBitCount * Header.dwWidth) shr 3;
for Y := 0 to Header.dwHeight-1 do begin
aStream.Read(TmpData^, RowSize);
Inc(TmpData, LineSize);
end;
end else
raise EglBitmapException.Create('LoadDDS - unsupported Pixelformat found.');

SetDataPointer(NewImage, ddsFormat, Header.dwWidth, Header.dwHeight);
result := true;
except
FreeMem(NewImage);
raise;
end;
finally
FreeAndNil(Converter);
end; end;
end; end;


@@ -7173,55 +7245,55 @@ end;
procedure TglBitmap.SaveDDS(const aStream: TStream); procedure TglBitmap.SaveDDS(const aStream: TStream);
var var
Header: TDDSHeader; Header: TDDSHeader;
Pix: TglBitmapPixelData;
FormatDesc: TFormatDescriptor; FormatDesc: TFormatDescriptor;
begin begin
//if not FormatIsUncompressed(InternalFormat) then
// raise EglBitmapUnsupportedFormatFormat.Create('SaveDDS - ' + UNSUPPORTED_FORMAT);
if not (ftDDS in FormatGetSupportedFiles(Format)) then
raise EglBitmapUnsupportedFormatFormat.Create('SaveDDS - ' + UNSUPPORTED_FORMAT);


(* TODO if Format = tfAlpha8 then
FORMAT_DESCRIPTORS[tfLuminance8].PreparePixel(Pix);
else *)
TFormatDescriptor.Get(Format).PreparePixel(Pix);
FormatDesc := TFormatDescriptor.Get(Format);


// Generell // Generell
FillChar(Header, SizeOf(Header), 0); FillChar(Header, SizeOf(Header), 0);
Header.dwMagic := DDS_MAGIC;
Header.dwSize := 124;
Header.dwFlags := DDSD_PITCH or DDSD_CAPS or DDSD_PIXELFORMAT;

if Width > 0 then begin
Header.dwWidth := Width;
Header.dwFlags := Header.dwFlags or DDSD_WIDTH;
end;

if Height > 0 then begin
Header.dwHeight := Height;
Header.dwFlags := Header.dwFlags or DDSD_HEIGHT;
end;
Header.dwSize := SizeOf(Header);
Header.dwFlags := DDSD_WIDTH or DDSD_HEIGHT or DDSD_CAPS or DDSD_PIXELFORMAT;


Header.dwPitchOrLinearSize := fRowSize;
Header.dwMipMapCount := 1;
Header.dwWidth := Max(1, Width);
Header.dwHeight := Max(1, Height);


// Caps // Caps
Header.Caps.dwCaps1 := DDSCAPS_TEXTURE; Header.Caps.dwCaps1 := DDSCAPS_TEXTURE;


// Pixelformat // Pixelformat
Header.PixelFormat.dwSize := Sizeof(Header.PixelFormat);
Header.PixelFormat.dwFlags := DDPF_RGB;
Header.PixelFormat.dwSize := sizeof(Header);
if (FormatDesc.IsCompressed) then begin
Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_FOURCC;
case Format of
tfS3tcDtx1RGBA: Header.PixelFormat.dwFourCC := D3DFMT_DXT1;
tfS3tcDtx3RGBA: Header.PixelFormat.dwFourCC := D3DFMT_DXT3;
tfS3tcDtx5RGBA: Header.PixelFormat.dwFourCC := D3DFMT_DXT5;
end;
end else if (Format in [tfAlpha8, tfAlpha16]) then begin
Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_ALPHA;
Header.PixelFormat.dwRGBBitCount := Round(FormatDesc.PixelSize * 8);
Header.PixelFormat.dwABitMask := FormatDesc.AlphaMask;
end else if (FormatDesc.RedMask = FormatDesc.GreenMask) and (FormatDesc.GreenMask = FormatDesc.BlueMask) then begin
Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_LUMINANCE;
Header.PixelFormat.dwRGBBitCount := Round(FormatDesc.PixelSize * 8);
Header.PixelFormat.dwRBitMask := FormatDesc.RedMask;
Header.PixelFormat.dwABitMask := FormatDesc.AlphaMask;
end else begin
Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_RGB;
Header.PixelFormat.dwRGBBitCount := Round(FormatDesc.PixelSize * 8);
Header.PixelFormat.dwRBitMask := FormatDesc.RedMask;
Header.PixelFormat.dwGBitMask := FormatDesc.GreenMask;
Header.PixelFormat.dwBBitMask := FormatDesc.BlueMask;
Header.PixelFormat.dwABitMask := FormatDesc.AlphaMask;
end;


(* TODO tfAlpha8
if FORMAT_DESCRIPTORS[Format].HasAlpha and (Format <> tfAlpha8) then
if (FormatDesc.HasAlpha) then
Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_ALPHAPIXELS; Header.PixelFormat.dwFlags := Header.PixelFormat.dwFlags or DDPF_ALPHAPIXELS;
*)

FormatDesc := TFormatDescriptor.Get(Format);
Header.PixelFormat.dwRGBBitCount := Trunc(FormatDesc.PixelSize * 8);
Header.PixelFormat.dwRBitMask := FormatDesc.RedMask;
Header.PixelFormat.dwGBitMask := FormatDesc.GreenMask;
Header.PixelFormat.dwBBitMask := FormatDesc.BlueMask;
Header.PixelFormat.dwABitMask := FormatDesc.AlphaMask;


aStream.Write(DDS_MAGIC, sizeof(DDS_MAGIC));
aStream.Write(Header, SizeOf(Header)); aStream.Write(Header, SizeOf(Header));
aStream.Write(Data^, FormatDesc.GetSize(Dimension)); aStream.Write(Data^, FormatDesc.GetSize(Dimension));
end; end;


Loading…
取消
儲存