Browse Source

* Bitfield support for LoadDDS

* refactored SaveDDS
master
Bergmann89 10 years ago
parent
commit
475288680c
1 changed files with 187 additions and 115 deletions
  1. +187
    -115
      glBitmap.pas

+ 187
- 115
glBitmap.pas View File

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

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

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

if (aFormat in [
//4 bbp
tfLuminance4,
@@ -1646,9 +1644,29 @@ begin
tfRGB10A2, tfRGBA8, tfBGR10A2, tfBGRA8]) then
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}
if aFormat in [
tfAlpha4, tfAlpha8, tfAlpha12, tfAlpha16,
@@ -6974,7 +6992,7 @@ end;
//DDS/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const
DDS_MAGIC = $20534444;
DDS_MAGIC: Cardinal = $20534444;

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

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

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

function GetDDSFormat: TglBitmapFormat;
var
fd: TFormatDescriptor;
i: Integer;
Range: TglBitmapColorRec;
match: Boolean;
begin
result := tfEmpty;
with Header.PixelFormat do begin
@@ -7062,66 +7086,79 @@ var
D3DFMT_DXT3: result := tfS3tcDtx3RGBA;
D3DFMT_DXT5: result := tfS3tcDtx5RGBA;
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;

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;

var
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;
FormatDesc: TFormatDescriptor;

begin
result := false;

// Header
result := false;
Converter := nil;
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)) <>
(DDSD_PIXELFORMAT or DDSD_CAPS or DDSD_WIDTH or DDSD_HEIGHT)) then
begin
@@ -7133,39 +7170,74 @@ begin
raise EglBitmapException.Create('LoadDDS - CubeMaps are not supported');

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
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.');

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;

@@ -7173,55 +7245,55 @@ end;
procedure TglBitmap.SaveDDS(const aStream: TStream);
var
Header: TDDSHeader;
Pix: TglBitmapPixelData;
FormatDesc: TFormatDescriptor;
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
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
Header.Caps.dwCaps1 := DDSCAPS_TEXTURE;

// 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;
*)

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(Data^, FormatDesc.GetSize(Dimension));
end;


Loading…
Cancel
Save