{ Vampyre Imaging Library by Marek Mauder https://github.com/galfar/imaginglib https://imaginglib.sourceforge.io - - - - - This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at https://mozilla.org/MPL/2.0. } { This unit contains functions for loading and saving Direct3D 9 textures using Imaging and for converting images to textures and vice versa.} unit ImagingDirect3D9; {$I ImagingOptions.inc} interface uses Windows, SysUtils, Classes, ImagingTypes, Imaging, ImagingFormats, ImagingUtility, Direct3D9; type { Contains some texture capabilities of Direct3D device.} TD3DTextureCaps = record PowerOfTwo: Boolean; CubePowerOfTwo: Boolean; VolumePowerOfTwo: Boolean; MaxWidth: LongInt; MaxHeight: LongInt; DXTCompression: Boolean; ATI3DcCompression: Boolean; MaxAnisotropy: LongInt; MaxSimultaneousTextures: LongInt; end; { Returns some texture capabilities of the given D3D device.} function GetDeviceTextureCaps(Device: IDirect3DDevice9; var Caps: TD3DTextureCaps): Boolean; { Returns True if the given Format is valid texture format for the given D3D device.} function IsD3DFormatSupported(Device: IDirect3DDevice9; Format: TD3DFormat): Boolean; { Returns D3D format equivalent to the given TImageFormatInfo. It returns D3DFMT_UNKNOWN if equivalent cannot be found. If returned ConversionTo is not the same as input format then image must be first converted to this format for the returned D3D format to be valid. You should also check if returned D3D format is supported by the current D3D device using IsD3DFormatSupported.} function ImageFormatToD3DFormat(const Format: TImageFormat; var ConversionTo: TImageFormat): TD3DFormat; { Returns TImageFormat equivalent to the given D3D format. If equivalent does not exist ifUnknown is returned.} function D3DFormatToImageFormat(Format: TD3DFormat): TImageFormat; { LoadD3DTextureFromFile and similar functions use these default values: All mipmap levels are created, Pool is D3DPOOL_MANAGED, Usage is 0, Format and size are taken from image.} { Creates D3D texture from image in file in format supported by Imaging. You can use CreatedWidth and Height parameters to query dimensions of created textures (it could differ from dimensions of source image).} function LoadD3DTextureFromFile(const FileName: string; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean; { Creates D3D texture from image in stream in format supported by Imaging. You can use CreatedWidth and Height parameters to query dimensions of created textures (it could differ from dimensions of source image).} function LoadD3DTextureFromStream(Stream: TStream; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean; { Creates D3D texture from image in memory in format supported by Imaging. You can use CreatedWidth and Height parameters to query dimensions of created textures (it could differ from dimensions of source image).} function LoadD3DTextureFromMemory(Data: Pointer; Size: LongInt; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean; { Converts TImageData structure to IDirect3DTexture9 texture. Input images is used as main mipmap level and additional requested levels are generated from this one. For the details on parameters look at CreateD3DTextureFromMultiImage function.} function CreateD3DTextureFromImage(const Image: TImageData; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width: LongInt = 0; Height: LongInt = 0; MipLevels: LongInt = 0; Usage: LongWord = 0; Format: TD3DFormat = D3DFMT_UNKNOWN; Pool: TD3DPool = D3DPOOL_MANAGED; CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean; { Converts images in TDymImageDataArray to one IDirect3DTexture9 texture. First image in array is used as main mipmap level and additional images are used as subsequent levels. If MipLevels is larger than number of images in array missing levels are automatically generated. If Device supports only power of two sized textures images are resized. If Format is D3DFMT_UNKNOWN then format of input image is used. If desired texture format is not supported by hardware default A8R8G8B8 format is used instead. Width and Height of 0 mean use width and height of main image. MipLevels set to 0 mean build all possible levels. For details on Usage and Pool parameters look at DirectX SDK docs. You can use CreatedWidth and CreatedHeight parameters to query dimensions of created texture's largest mipmap level (it could differ from dimensions of source image).} function CreateD3DTextureFromMultiImage(const Images: TDynImageDataArray; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width: LongInt = 0; Height: LongInt = 0; MipLevels: LongInt = 0; Usage: LongWord = 0; Format: TD3DFormat = D3DFMT_UNKNOWN; Pool: TD3DPool = D3DPOOL_MANAGED; CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean; { Saves D3D texture to file in one of formats supported by Imaging. Saves all present mipmap levels.} function SaveD3DTextureToFile(const FileName: string; const Texture: IDirect3DTexture9): Boolean; { Saves D3D texture to stream in one of formats supported by Imaging. Saves all present mipmap levels.} function SaveD3DTextureToStream(const Ext: string; Stream: TStream; const Texture: IDirect3DTexture9): Boolean; { Saves D3D texture to memory in one of formats supported by Imaging. Saves all present mipmap levels.} function SaveD3DTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: IDirect3DTexture9): Boolean; { Converts main level of the D3D texture to TImageData structure. OverrideFormat can be used to convert output image to the specified format rather than use the format taken from D3D texture, ifUnknown means no conversion.} function CreateImageFromD3DTexture(const Texture: IDirect3DTexture9; var Image: TImageData; OverrideFormat: TImageFormat = ifUnknown): Boolean; { Converts D3D texture to TDynImageDataArray array of images. You can specify how many mipmap levels of the input texture you want to be converted (default is all levels). OverrideFormat can be used to convert output images to the specified format rather than use the format taken from D3D texture, ifUnknown means no conversion.} function CreateMultiImageFromD3DTexture(const Texture: IDirect3DTexture9; var Images: TDynImageDataArray; MipLevels: LongInt = 0; OverrideFormat: TImageFormat = ifUnknown): Boolean; { Creates contents of Image to D3D surface. Surface must exist before calling this function so it can be used to fill various types of surfaces (textures surfaces, offscreen, depth buffer, ...). Surface must be lockable for function to work.} function CreateD3DSurfaceFromImage(const Image: TImageData; Surface: IDirect3DSurface9): Boolean; { Creates image filled with contents of input D3D surface. Surface must be lockable for function to work.} function CreateImageFromD3DSurface(Surface: IDirect3DSurface9; var Image: TImageData): Boolean; const D3DFMT_ATI1 = TD3DFormat(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or (Byte('1') shl 24)); D3DFMT_ATI2 = TD3DFormat(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or (Byte('2') shl 24)); implementation const DefaultUsage = 0; DefaultPool = D3DPOOL_MANAGED; function GetDeviceTextureCaps(Device: IDirect3DDevice9; var Caps: TD3DTextureCaps): Boolean; var D3DCaps: TD3DCaps9; begin FillChar(Caps, SizeOf(Caps), 0); Result := Device <> nil; // Get D3D Device Caps and fill our caps if Result and (Device.GetDeviceCaps(D3DCaps) = D3D_OK) then begin Caps.PowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_POW2) = D3DPTEXTURECAPS_POW2; Caps.CubePowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_CUBEMAP_POW2) = D3DPTEXTURECAPS_CUBEMAP_POW2; Caps.VolumePowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_VOLUMEMAP_POW2) = D3DPTEXTURECAPS_VOLUMEMAP_POW2; Caps.MaxWidth := D3DCaps.MaxTextureWidth; Caps.MaxHeight := D3DCaps.MaxTextureHeight; if (D3DCaps.TextureFilterCaps and D3DPTFILTERCAPS_MINFANISOTROPIC) = D3DPTFILTERCAPS_MINFANISOTROPIC then Caps.MaxAnisotropy := D3DCaps.MaxAnisotropy else Caps.MaxAnisotropy := 0; Caps.MaxSimultaneousTextures := D3DCaps.MaxSimultaneousTextures; // Texture format caps Caps.DXTCompression := IsD3DFormatSupported(Device, D3DFMT_DXT1) and IsD3DFormatSupported(Device, D3DFMT_DXT3) and IsD3DFormatSupported(Device, D3DFMT_DXT5); Caps.ATI3DcCompression := IsD3DFormatSupported(Device, D3DFMT_ATI1) and IsD3DFormatSupported(Device, D3DFMT_ATI2); end; end; function IsD3DFormatSupported(Device: IDirect3DDevice9; Format: TD3DFormat): Boolean; var Direct3D: IDirect3D9; Mode: TD3DDisplayMode; Hr: HResult; begin Result := False; if Device <> nil then begin Device.GetDirect3D(Direct3D); if Direct3D <> nil then begin Direct3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, Mode); Hr := Direct3D.CheckDeviceFormat(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, Mode.Format, 0, D3DRTYPE_TEXTURE, Format); Result := Succeeded(Hr); end; end; end; function ImageFormatToD3DFormat(const Format: TImageFormat; var ConversionTo: TImageFormat): TD3DFormat; begin Result := D3DFMT_UNKNOWN; ConversionTo := Format; case Format of ifIndex8: Result := D3DFMT_P8; ifGray8: Result := D3DFMT_L8; ifA8Gray8: Result := D3DFMT_A8L8; ifGray16: Result := D3DFMT_L16; ifGray32, ifGray64: begin Result := D3DFMT_L16; ConversionTo := ifGray16; end; ifA16Gray16: begin Result := D3DFMT_A8L8; ConversionTo := ifA8Gray8; end; ifX5R1G1B1: begin Result := D3DFMT_R3G3B2; ConversionTo := ifR3G3B2; end; ifR3G3B2: Result := D3DFMT_R3G3B2; ifR5G6B5: Result := D3DFMT_R5G6B5; ifA1R5G5B5: Result := D3DFMT_A1R5G5B5; ifA4R4G4B4: Result := D3DFMT_A4R4G4B4; ifX1R5G5B5: Result := D3DFMT_X1R5G5B5; ifX4R4G4B4: Result := D3DFMT_X4R4G4B4; ifR8G8B8: Result := D3DFMT_R8G8B8; ifA8R8G8B8: Result := D3DFMT_A8R8G8B8; ifX8R8G8B8: Result := D3DFMT_X8R8G8B8; ifR16G16B16, ifA16R16G16B16, ifB16G16R16: begin Result := D3DFMT_A16B16G16R16; ConversionTo := ifA16B16G16R16; end; ifA16B16G16R16: Result := D3DFMT_A16B16G16R16; ifR32F: Result := D3DFMT_R32F; ifA32B32G32R32F: Result := D3DFMT_A32B32G32R32F; ifA32R32G32B32F: begin Result := D3DFMT_A32B32G32R32F; ConversionTo := ifA32B32G32R32F; end; ifR16F: Result := D3DFMT_R16F; ifA16B16G16R16F: Result := D3DFMT_A16B16G16R16F; ifA16R16G16B16F: begin Result := D3DFMT_A16B16G16R16F; ConversionTo := ifA16B16G16R16F; end; ifDXT1: Result := D3DFMT_DXT1; ifDXT3: Result := D3DFMT_DXT3; ifDXT5: Result := D3DFMT_DXT5; ifATI1N: Result := D3DFMT_ATI1; ifATI2N: Result := D3DFMT_ATI2; end; end; function D3DFormatToImageFormat(Format: TD3DFormat): TImageFormat; begin Result := ifUnknown; case Format of D3DFMT_P8: Result := ifIndex8; D3DFMT_A8, D3DFMT_L8: Result := ifGray8; D3DFMT_A8L8, D3DFMT_V8U8: Result := ifA8Gray8; D3DFMT_L16: Result := ifGray16; D3DFMT_R3G3B2: Result := ifR3G3B2; D3DFMT_R5G6B5: Result := ifR5G6B5; D3DFMT_X1R5G5B5: Result := ifX1R5G5B5; D3DFMT_A1R5G5B5: Result := ifA1R5G5B5; D3DFMT_A4R4G4B4: Result := ifA4R4G4B4; D3DFMT_X4R4G4B4: Result := ifX4R4G4B4; D3DFMT_R8G8B8: Result := ifR8G8B8; D3DFMT_A8R8G8B8, D3DFMT_Q8W8V8U8, D3DFMT_A8B8G8R8: Result := ifA8R8G8B8; D3DFMT_X8R8G8B8, D3DFMT_X8L8V8U8, D3DFMT_X8B8G8R8: Result := ifX8R8G8B8; D3DFMT_A16B16G16R16, D3DFMT_Q16W16V16U16: Result := ifA16B16G16R16; D3DFMT_R32F: Result := ifR32F; D3DFMT_A32B32G32R32F: Result := ifA32B32G32R32F; D3DFMT_R16F: Result := ifR16F; D3DFMT_A16B16G16R16F: Result := ifA16B16G16R16F; D3DFMT_DXT1: Result := ifDXT1; D3DFMT_DXT3: Result := ifDXT3; D3DFMT_DXT5: Result := ifDXT5; D3DFMT_ATI1: Result := ifATI1N; D3DFMT_ATI2: Result := ifATI2N; end; end; function LoadD3DTextureFromFile(const FileName: string; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth, CreatedHeight: PLongInt): Boolean; var Images: TDynImageDataArray; begin if LoadMultiImageFromFile(FileName, Images) and (Length(Images) > 0) then begin Result := CreateD3DTextureFromMultiImage(Images, Device, Texture, Images[0].Width, Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN, DefaultPool, CreatedWidth, CreatedHeight); end else Result := False; FreeImagesInArray(Images); end; function LoadD3DTextureFromStream(Stream: TStream; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth, CreatedHeight: PLongInt): Boolean; var Images: TDynImageDataArray; begin if LoadMultiImageFromStream(Stream, Images) and (Length(Images) > 0) then begin Result := CreateD3DTextureFromMultiImage(Images, Device, Texture, Images[0].Width, Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN, DefaultPool, CreatedWidth, CreatedHeight); end else Result := False; FreeImagesInArray(Images); end; function LoadD3DTextureFromMemory(Data: Pointer; Size: LongInt; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; CreatedWidth, CreatedHeight: PLongInt): Boolean; var Images: TDynImageDataArray; begin if LoadMultiImageFromMemory(Data, Size, Images) and (Length(Images) > 0) then begin Result := CreateD3DTextureFromMultiImage(Images, Device, Texture, Images[0].Width, Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN, DefaultPool, CreatedWidth, CreatedHeight); end else Result := False; FreeImagesInArray(Images); end; function CreateD3DTextureFromImage(const Image: TImageData; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width, Height, MipLevels: LongInt; Usage: LongWord; Format: TD3DFormat; Pool: TD3DPool; CreatedWidth, CreatedHeight: PLongInt): Boolean; var Arr: TDynImageDataArray; begin // Just calls function operating on image arrays SetLength(Arr, 1); Arr[0] := Image; Result := CreateD3DTextureFromMultiImage(Arr, Device, Texture, Width, Height, MipLevels, Usage, Format, Pool, CreatedWidth, CreatedHeight); end; procedure FillLockedRectWithImage(var Rect: TD3DLockedRect; const Image: TImageData); var I, LineBytes: LongInt; Info: TImageFormatInfo; begin GetImageFormatInfo(Image.Format, Info); LineBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1); // Pixels of the image are copied to D3D texture if (not Info.IsSpecial) and (LineBytes < Rect.Pitch) then begin for I := 0 to Image.Height - 1 do Move(PByteArray(Image.Bits)[I * LineBytes], PByteArray(Rect.pBits)[I * Rect.Pitch], LineBytes); end else Move(Image.Bits^, Rect.pBits^, Image.Size); end; function CreateD3DTextureFromMultiImage(const Images: TDynImageDataArray; Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width, Height, MipLevels: LongInt; Usage: LongWord; Format: TD3DFormat; Pool: TD3DPool; CreatedWidth, CreatedHeight: PLongInt): Boolean; var I, PossibleLevels, ExistingLevels, CurrentWidth, CurrentHeight: LongInt; Caps: TD3DTextureCaps; Rect: TD3DLockedRect; ConvTo: TImageFormat; LevelsArray: TDynImageDataArray; NeedsResize, NeedsConvert: Boolean; begin Texture := nil; ExistingLevels := 0; Result := False; // Get texture caps of the current device and test if there is anything to convert if GetDeviceTextureCaps(Device, Caps) and (Length(Images) > 0) then try // First check desired size and modify it if necessary if Width <= 0 then Width := Images[0].Width; if Height <= 0 then Height := Images[0].Height; if Caps.PowerOfTwo then begin // If device supports only power of 2 texture sizes Width := NextPow2(Width); Height := NextPow2(Height); end; Width := ClampInt(Width, 1, Caps.MaxWidth); Height := ClampInt(Height, 1, Caps.MaxHeight); // Get various mipmap level counts and modify // desired MipLevels if its value is invalid ExistingLevels := Length(Images); PossibleLevels := GetNumMipMapLevels(Width, Height); if (MipLevels < 1) or (MipLevels > PossibleLevels) then MipLevels := PossibleLevels; // Now determine which image format will be used if Format = D3DFMT_UNKNOWN then begin // D3D texture format is not explicitly defined so we use // the current format of the input image Format := ImageFormatToD3DFormat(Images[0].Format, ConvTo); // Format is now either D3DFMT_UNKNOWN or some valid format and // ConvTo contains format to which input image must be converted first // (if ConvTo and input image's format differ). // We must also test if returned D3D format is supported by D3D device if (Format = D3DFMT_UNKNOWN) or not IsD3DFormatSupported(Device, Format) then begin Format := D3DFMT_A8R8G8B8; ConvTo := ifA8R8G8B8; end; end else begin // Image format corresponding to desired D3D format is either found // and image is converted to it (if the image is not in this format already) // or it is not found (or not supported by hardware) and default format is used ConvTo := D3DFormatToImageFormat(Format); if (ConvTo = ifUnknown) or not IsD3DFormatSupported(Device, Format) then begin Format := D3DFMT_A8R8G8B8; ConvTo := ifA8R8G8B8; end; end; // Prepare array for mipmap levels SetLength(LevelsArray, MipLevels); CurrentWidth := Width; CurrentHeight := Height; for I := 0 to MipLevels - 1 do begin // Check if we can use input image array as a source for this mipmap level if I < ExistingLevels then begin // Check if input image for this mipmap level has the right // size and format NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight)); NeedsConvert := not (Images[I].Format = ConvTo); if NeedsResize or NeedsConvert then begin // Input image must be resized or converted to different format // to become valid mipmap level CloneImage(Images[I], LevelsArray[I]); if NeedsConvert then ConvertImage(LevelsArray[I], ConvTo); if NeedsResize then ResizeImage(LevelsArray[I], CurrentWidth, CurrentHeight, rfBilinear); end else // Input image can be used without any changes LevelsArray[I] := Images[I]; end else begin // This mipmap level is not present in the input image array // so we create a new level FillMipMapLevel(LevelsArray[I - 1], CurrentWidth, CurrentHeight, LevelsArray[I]); end; // Calculate width and height of the next mipmap level CurrentWidth := ClampInt(CurrentWidth div 2, 1, CurrentWidth); CurrentHeight := ClampInt(CurrentHeight div 2, 1, CurrentHeight); end; // Finally create D3D texture object if Succeeded(Device.CreateTexture(LevelsArray[0].Width, LevelsArray[0].Height, MipLevels, Usage, Format, Pool, Texture, nil)) then begin // Fill each mipmap level for I := 0 to MipLevels - 1 do if Succeeded(Texture.LockRect(I, Rect, nil, 0)) then begin FillLockedRectWithImage(Rect, LevelsArray[I]); Texture.UnlockRect(I); end; Result := True; end; // If user is interested in width and height of created texture lets // give him that if CreatedWidth <> nil then CreatedWidth^ := LevelsArray[0].Width; if CreatedHeight <> nil then CreatedHeight^ := LevelsArray[0].Height; finally // Free local image copies for I := 0 to Length(LevelsArray) - 1 do begin if ((I < ExistingLevels) and (LevelsArray[I].Bits <> Images[I].Bits)) or (I >= ExistingLevels) then FreeImage(LevelsArray[I]); end; end; end; function SaveD3DTextureToFile(const FileName: string; const Texture: IDirect3DTexture9): Boolean; var Arr: TDynImageDataArray; Fmt: TImageFileFormat; IsDDS: Boolean; begin Result := CreateMultiImageFromD3DTexture(Texture, Arr); if Result then begin Fmt := FindImageFileFormatByName(FileName); if Fmt <> nil then begin IsDDS := SameText(Fmt.Extensions[0], 'dds'); if IsDDS then begin PushOptions; SetOption(ImagingDDSSaveMipMapCount, Length(Arr)); end; Result := SaveMultiImageToFile(FileName, Arr); if IsDDS then PopOptions; end; end; end; function SaveD3DTextureToStream(const Ext: string; Stream: TStream; const Texture: IDirect3DTexture9): Boolean; var Arr: TDynImageDataArray; Fmt: TImageFileFormat; IsDDS: Boolean; begin Result := CreateMultiImageFromD3DTexture(Texture, Arr); if Result then begin Fmt := FindImageFileFormatByExt(Ext); if Fmt <> nil then begin IsDDS := SameText(Fmt.Extensions[0], 'dds'); if IsDDS then begin PushOptions; SetOption(ImagingDDSSaveMipMapCount, Length(Arr)); end; Result := SaveMultiImageToStream(Ext, Stream, Arr); if IsDDS then PopOptions; end; end; end; function SaveD3DTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: IDirect3DTexture9): Boolean; var Arr: TDynImageDataArray; Fmt: TImageFileFormat; IsDDS: Boolean; begin Result := CreateMultiImageFromD3DTexture(Texture, Arr); if Result then begin Fmt := FindImageFileFormatByExt(Ext); if Fmt <> nil then begin IsDDS := SameText(Fmt.Extensions[0], 'dds'); if IsDDS then begin PushOptions; SetOption(ImagingDDSSaveMipMapCount, Length(Arr)); end; Result := SaveMultiImageToMemory(Ext, Data, Size, Arr); if IsDDS then PopOptions; end; end; end; function CreateImageFromD3DTexture(const Texture: IDirect3DTexture9; var Image: TImageData; OverrideFormat: TImageFormat): Boolean; var Arr: TDynImageDataArray; begin // Just calls function operating on image arrays FreeImage(Image); SetLength(Arr, 1); Result := CreateMultiImageFromD3DTexture(Texture, Arr, 1, OverrideFormat); Image := Arr[0]; end; procedure FillImageWithLockedRect(var Image: TImageData; const Rect: TD3DLockedRect); var I, LineBytes: LongInt; Info: TImageFormatInfo; begin GetImageFormatInfo(Image.Format, Info); LineBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1); // Pixels are copied from D3D texture to the image if (not Info.IsSpecial) and (LineBytes < Rect.Pitch) then begin for I := 0 to Image.Height - 1 do Move(PByteArray(Rect.pBits)[I * Rect.Pitch], PByteArray(Image.Bits)[I * LineBytes], LineBytes); end else Move(Rect.pBits^, Image.Bits^, Image.Size); end; function CreateMultiImageFromD3DTexture(const Texture: IDirect3DTexture9; var Images: TDynImageDataArray; MipLevels: LongInt; OverrideFormat: TImageFormat): Boolean; var Rect: TD3DLockedRect; Desc: TD3DSurfaceDesc; I,ExistingLevels: LongInt; CurrentFormat: TImageFormat; Info: TImageFormatInfo; begin FreeImagesInArray(Images); SetLength(Images, 0); Result := False; if Texture <> nil then begin // Check if desired mipmap level count is valid ExistingLevels := Texture.GetLevelCount; if (MipLevels <= 0) or (MipLevels > ExistingLevels) then MipLevels := ExistingLevels; Texture.GetLevelDesc(0, Desc); // Try to find image format compatible with d3d texture's format CurrentFormat := D3DFormatToImageFormat(Desc.Format); // Exit if no compatible image format is found if CurrentFormat = ifUnknown then Exit; SetLength(Images, MipLevels); GetImageFormatInfo(CurrentFormat, Info); for I := 0 to MipLevels - 1 do begin if Failed(Texture.LockRect(I, Rect, nil, D3DLOCK_READONLY)) then Exit; Texture.GetLevelDesc(I, Desc); // Create image for the current mipmap level and copy texture data to it NewImage(Desc.Width, Desc.Height, CurrentFormat, Images[I]); FillImageWithLockedRect(Images[I], Rect); // If override format is set each mipmap level is converted to it if OverrideFormat <> ifUnknown then ConvertImage(Images[I], OverrideFormat); Texture.UnlockRect(I); end; Result := True; end; end; function CreateD3DSurfaceFromImage(const Image: TImageData; Surface: IDirect3DSurface9): Boolean; var ConvTo: TImageFormat; Desc: TD3DSurfaceDesc; Rect: TD3DLockedRect; WorkImage: TImageData; begin Result := False; if (Surface = nil) or not TestImage(Image) then Exit; // Get surface's format and find Imaging data format match Surface.GetDesc(Desc); ConvTo := D3DFormatToImageFormat(Desc.Format); // If no Imaging data format was found we must exit if ConvTo = ifUnknown then Exit; if (LongInt(Desc.Width) <> Image.Width) or (LongInt(Desc.Height) <> Image.Height) or (Image.Format <> ConvTo) then begin // Source image has different dimensions or format than dest surface, // working image is created InitImage(WorkImage); NewImage(Desc.Width, Desc.Height, ConvTo, WorkImage); StretchRect(Image, 0, 0, Image.Width, Image.Height, WorkImage, 0, 0, WorkImage.Width, WorkImage.Height, rfBilinear); end else WorkImage := Image; try // Lock surface and fill it with image if Succeeded(Surface.LockRect(Rect, nil, 0)) then begin FillLockedRectWithImage(Rect, WorkImage); Surface.UnlockRect; Result := True; end; finally // Free working image if it is not reference to source image if WorkImage.Bits <> Image.Bits then FreeImage(WorkImage); end; end; function CreateImageFromD3DSurface(Surface: IDirect3DSurface9; var Image: TImageData): Boolean; var CurrentFormat: TImageFormat; Desc: TD3DSurfaceDesc; Rect: TD3DLockedRect; begin Result := False; FreeImage(Image); if Surface = nil then Exit; Surface.GetDesc(Desc); CurrentFormat := D3DFormatToImageFormat(Desc.Format); // Exit if no compatible image format is found if CurrentFormat = ifUnknown then Exit; if Succeeded(Surface.LockRect(Rect, nil, D3DLOCK_READONLY)) then begin // If surface was successfully locked a new image is created // and surface's contents are copied to it NewImage(Desc.Width, Desc.Height, CurrentFormat, Image); FillImageWithLockedRect(Image, Rect); Surface.UnlockRect; Result := True; end; end; { File Notes: -- TODOS ---------------------------------------------------- - support for cube and volume maps -- 0.25.0 Changes/Bug Fixes --------------------------------- - Added support for 3Dc compressed texture formats. - Added detection of 3Dc support to texture caps. -- 0.21 Changes/Bug Fixes ----------------------------------- - Added CreatedWidth and CreatedHeight parameters to most LoadD3DTextureFromXXX/CreateD3DTextureFromXXX functions. -- 0.19 Changes/Bug Fixes ----------------------------------- - fixed bug in CreateGLTextureFromMultiImage which caused assert failure when creating mipmaps (using FillMipMapLevel) for DXTC formats - added support for 16bit half-float texture formats -- 0.17 Changes/Bug Fixes ----------------------------------- - D3D surface support - fill surface with image and vice versa - more texture caps added - filtered mipmap creation -- 0.15 Changes/Bug Fixes ----------------------------------- - unit created and initial stuff added } end.