////////////////////////////////////////////////////////////////////////////////////////////////////
TglBitmapFormat = (
- tfEmpty = 0,
+ tfEmpty = 0, //must be smallest value!
tfAlpha4,
tfAlpha8,
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function FormatGetSupportedFiles(const aFormat: TglBitmapFormat): TglBitmapFileTypes;
begin
- result := [ftDDS];
-
if (aFormat in [
//4 bbp
tfLuminance4,
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,
//DDS/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
const
- DDS_MAGIC = $20534444;
+ DDS_MAGIC: Cardinal = $20534444;
// DDS_header.dwFlags
DDSD_CAPS = $00000001;
// DDS_header.sPixelFormat.dwFlags
DDPF_ALPHAPIXELS = $00000001;
+ DDPF_ALPHA = $00000002;
DDPF_FOURCC = $00000004;
DDPF_INDEXED = $00000020;
DDPF_RGB = $00000040;
end;
TDDSHeader = packed record
- dwMagic: Cardinal;
dwSize: Cardinal;
dwFlags: Cardinal;
dwHeight: Cardinal;
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
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
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;
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;