From 475288680c6bdf18f91e97e17707c1553b070c01 Mon Sep 17 00:00:00 2001 From: Bergmann89 Date: Tue, 12 Nov 2013 17:50:35 +0100 Subject: [PATCH] * Bitfield support for LoadDDS * refactored SaveDDS --- glBitmap.pas | 302 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 187 insertions(+), 115 deletions(-) diff --git a/glBitmap.pas b/glBitmap.pas index 72c81e0..e1e591d 100644 --- a/glBitmap.pas +++ b/glBitmap.pas @@ -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; -- 2.1.4