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