| @@ -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; | ||||