ImagingDirect3D9.pas 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { This unit contains functions for loading and saving Direct3D 9 textures
  12. using Imaging and for converting images to textures and vice versa.}
  13. unit ImagingDirect3D9;
  14. {$I ImagingOptions.inc}
  15. interface
  16. uses
  17. Windows, SysUtils, Classes, ImagingTypes, Imaging, ImagingFormats,
  18. ImagingUtility, Direct3D9;
  19. type
  20. { Contains some texture capabilities of Direct3D device.}
  21. TD3DTextureCaps = record
  22. PowerOfTwo: Boolean;
  23. CubePowerOfTwo: Boolean;
  24. VolumePowerOfTwo: Boolean;
  25. MaxWidth: LongInt;
  26. MaxHeight: LongInt;
  27. DXTCompression: Boolean;
  28. ATI3DcCompression: Boolean;
  29. MaxAnisotropy: LongInt;
  30. MaxSimultaneousTextures: LongInt;
  31. end;
  32. { Returns some texture capabilities of the given D3D device.}
  33. function GetDeviceTextureCaps(Device: IDirect3DDevice9; var Caps: TD3DTextureCaps): Boolean;
  34. { Returns True if the given Format is valid texture format for the given D3D device.}
  35. function IsD3DFormatSupported(Device: IDirect3DDevice9; Format: TD3DFormat): Boolean;
  36. { Returns D3D format equivalent to the given TImageFormatInfo. It returns D3DFMT_UNKNOWN
  37. if equivalent cannot be found. If returned ConversionTo is not the same
  38. as input format then image must be first converted to this format for
  39. the returned D3D format to be valid. You should also check if returned D3D
  40. format is supported by the current D3D device using IsD3DFormatSupported.}
  41. function ImageFormatToD3DFormat(const Format: TImageFormat; var ConversionTo: TImageFormat): TD3DFormat;
  42. { Returns TImageFormat equivalent to the given D3D format. If equivalent does
  43. not exist ifUnknown is returned.}
  44. function D3DFormatToImageFormat(Format: TD3DFormat): TImageFormat;
  45. { LoadD3DTextureFromFile and similar functions use these default values:
  46. All mipmap levels are created, Pool is D3DPOOL_MANAGED,
  47. Usage is 0, Format and size are taken from image.}
  48. { Creates D3D texture from image in file in format supported by Imaging.
  49. You can use CreatedWidth and Height parameters to query dimensions of created textures
  50. (it could differ from dimensions of source image).}
  51. function LoadD3DTextureFromFile(const FileName: string; Device: IDirect3DDevice9;
  52. var Texture: IDirect3DTexture9; CreatedWidth: PLongInt = nil;
  53. CreatedHeight: PLongInt = nil): Boolean;
  54. { Creates D3D texture from image in stream in format supported by Imaging.
  55. You can use CreatedWidth and Height parameters to query dimensions of created textures
  56. (it could differ from dimensions of source image).}
  57. function LoadD3DTextureFromStream(Stream: TStream; Device: IDirect3DDevice9;
  58. var Texture: IDirect3DTexture9; CreatedWidth: PLongInt = nil;
  59. CreatedHeight: PLongInt = nil): Boolean;
  60. { Creates D3D texture from image in memory in format supported by Imaging.
  61. You can use CreatedWidth and Height parameters to query dimensions of created textures
  62. (it could differ from dimensions of source image).}
  63. function LoadD3DTextureFromMemory(Data: Pointer; Size: LongInt;
  64. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9;
  65. CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean;
  66. { Converts TImageData structure to IDirect3DTexture9 texture.
  67. Input images is used as main mipmap level and additional requested
  68. levels are generated from this one. For the details on parameters
  69. look at CreateD3DTextureFromMultiImage function.}
  70. function CreateD3DTextureFromImage(const Image: TImageData;
  71. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width: LongInt = 0;
  72. Height: LongInt = 0; MipLevels: LongInt = 0; Usage: LongWord = 0;
  73. Format: TD3DFormat = D3DFMT_UNKNOWN; Pool: TD3DPool = D3DPOOL_MANAGED;
  74. CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean;
  75. { Converts images in TDymImageDataArray to one IDirect3DTexture9 texture.
  76. First image in array is used as main mipmap level and additional images
  77. are used as subsequent levels. If MipLevels is larger than number of images
  78. in array missing levels are automatically generated.
  79. If Device supports only power of two sized textures images are resized.
  80. If Format is D3DFMT_UNKNOWN then format of input image is used.
  81. If desired texture format is not supported by hardware default
  82. A8R8G8B8 format is used instead.
  83. Width and Height of 0 mean use width and height of main image.
  84. MipLevels set to 0 mean build all possible levels. For details on
  85. Usage and Pool parameters look at DirectX SDK docs.
  86. You can use CreatedWidth and CreatedHeight parameters to query dimensions of
  87. created texture's largest mipmap level (it could differ from dimensions
  88. of source image).}
  89. function CreateD3DTextureFromMultiImage(const Images: TDynImageDataArray;
  90. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width: LongInt = 0;
  91. Height: LongInt = 0; MipLevels: LongInt = 0; Usage: LongWord = 0;
  92. Format: TD3DFormat = D3DFMT_UNKNOWN; Pool: TD3DPool = D3DPOOL_MANAGED;
  93. CreatedWidth: PLongInt = nil; CreatedHeight: PLongInt = nil): Boolean;
  94. { Saves D3D texture to file in one of formats supported by Imaging.
  95. Saves all present mipmap levels.}
  96. function SaveD3DTextureToFile(const FileName: string; const Texture: IDirect3DTexture9): Boolean;
  97. { Saves D3D texture to stream in one of formats supported by Imaging.
  98. Saves all present mipmap levels.}
  99. function SaveD3DTextureToStream(const Ext: string; Stream: TStream; const Texture: IDirect3DTexture9): Boolean;
  100. { Saves D3D texture to memory in one of formats supported by Imaging.
  101. Saves all present mipmap levels.}
  102. function SaveD3DTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: IDirect3DTexture9): Boolean;
  103. { Converts main level of the D3D texture to TImageData structure. OverrideFormat
  104. can be used to convert output image to the specified format rather
  105. than use the format taken from D3D texture, ifUnknown means no conversion.}
  106. function CreateImageFromD3DTexture(const Texture: IDirect3DTexture9;
  107. var Image: TImageData; OverrideFormat: TImageFormat = ifUnknown): Boolean;
  108. { Converts D3D texture to TDynImageDataArray array of images. You can specify
  109. how many mipmap levels of the input texture you want to be converted
  110. (default is all levels). OverrideFormat can be used to convert output images to
  111. the specified format rather than use the format taken from D3D texture,
  112. ifUnknown means no conversion.}
  113. function CreateMultiImageFromD3DTexture(const Texture: IDirect3DTexture9;
  114. var Images: TDynImageDataArray; MipLevels: LongInt = 0;
  115. OverrideFormat: TImageFormat = ifUnknown): Boolean;
  116. { Creates contents of Image to D3D surface. Surface must exist before calling this
  117. function so it can be used to fill various types of surfaces (textures surfaces,
  118. offscreen, depth buffer, ...). Surface must be lockable for function to work.}
  119. function CreateD3DSurfaceFromImage(const Image: TImageData; Surface: IDirect3DSurface9): Boolean;
  120. { Creates image filled with contents of input D3D surface.
  121. Surface must be lockable for function to work.}
  122. function CreateImageFromD3DSurface(Surface: IDirect3DSurface9; var Image: TImageData): Boolean;
  123. const
  124. D3DFMT_ATI1 = TD3DFormat(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  125. (Byte('1') shl 24));
  126. D3DFMT_ATI2 = TD3DFormat(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  127. (Byte('2') shl 24));
  128. implementation
  129. const
  130. DefaultUsage = 0;
  131. DefaultPool = D3DPOOL_MANAGED;
  132. function GetDeviceTextureCaps(Device: IDirect3DDevice9;
  133. var Caps: TD3DTextureCaps): Boolean;
  134. var
  135. D3DCaps: TD3DCaps9;
  136. begin
  137. FillChar(Caps, SizeOf(Caps), 0);
  138. Result := Device <> nil;
  139. // Get D3D Device Caps and fill our caps
  140. if Result and (Device.GetDeviceCaps(D3DCaps) = D3D_OK) then
  141. begin
  142. Caps.PowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_POW2) = D3DPTEXTURECAPS_POW2;
  143. Caps.CubePowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_CUBEMAP_POW2) = D3DPTEXTURECAPS_CUBEMAP_POW2;
  144. Caps.VolumePowerOfTwo := (D3DCaps.TextureCaps and D3DPTEXTURECAPS_VOLUMEMAP_POW2) = D3DPTEXTURECAPS_VOLUMEMAP_POW2;
  145. Caps.MaxWidth := D3DCaps.MaxTextureWidth;
  146. Caps.MaxHeight := D3DCaps.MaxTextureHeight;
  147. if (D3DCaps.TextureFilterCaps and D3DPTFILTERCAPS_MINFANISOTROPIC) = D3DPTFILTERCAPS_MINFANISOTROPIC then
  148. Caps.MaxAnisotropy := D3DCaps.MaxAnisotropy
  149. else
  150. Caps.MaxAnisotropy := 0;
  151. Caps.MaxSimultaneousTextures := D3DCaps.MaxSimultaneousTextures;
  152. // Texture format caps
  153. Caps.DXTCompression := IsD3DFormatSupported(Device, D3DFMT_DXT1) and
  154. IsD3DFormatSupported(Device, D3DFMT_DXT3) and IsD3DFormatSupported(Device, D3DFMT_DXT5);
  155. Caps.ATI3DcCompression := IsD3DFormatSupported(Device, D3DFMT_ATI1) and
  156. IsD3DFormatSupported(Device, D3DFMT_ATI2);
  157. end;
  158. end;
  159. function IsD3DFormatSupported(Device: IDirect3DDevice9; Format: TD3DFormat): Boolean;
  160. var
  161. Direct3D: IDirect3D9;
  162. Mode: TD3DDisplayMode;
  163. Hr: HResult;
  164. begin
  165. Result := False;
  166. if Device <> nil then
  167. begin
  168. Device.GetDirect3D(Direct3D);
  169. if Direct3D <> nil then
  170. begin
  171. Direct3D.GetAdapterDisplayMode(D3DADAPTER_DEFAULT, Mode);
  172. Hr := Direct3D.CheckDeviceFormat(D3DADAPTER_DEFAULT,
  173. D3DDEVTYPE_HAL, Mode.Format, 0, D3DRTYPE_TEXTURE, Format);
  174. Result := Succeeded(Hr);
  175. end;
  176. end;
  177. end;
  178. function ImageFormatToD3DFormat(const Format: TImageFormat; var ConversionTo: TImageFormat): TD3DFormat;
  179. begin
  180. Result := D3DFMT_UNKNOWN;
  181. ConversionTo := Format;
  182. case Format of
  183. ifIndex8: Result := D3DFMT_P8;
  184. ifGray8: Result := D3DFMT_L8;
  185. ifA8Gray8: Result := D3DFMT_A8L8;
  186. ifGray16: Result := D3DFMT_L16;
  187. ifGray32,
  188. ifGray64:
  189. begin
  190. Result := D3DFMT_L16;
  191. ConversionTo := ifGray16;
  192. end;
  193. ifA16Gray16:
  194. begin
  195. Result := D3DFMT_A8L8;
  196. ConversionTo := ifA8Gray8;
  197. end;
  198. ifX5R1G1B1:
  199. begin
  200. Result := D3DFMT_R3G3B2;
  201. ConversionTo := ifR3G3B2;
  202. end;
  203. ifR3G3B2: Result := D3DFMT_R3G3B2;
  204. ifR5G6B5: Result := D3DFMT_R5G6B5;
  205. ifA1R5G5B5: Result := D3DFMT_A1R5G5B5;
  206. ifA4R4G4B4: Result := D3DFMT_A4R4G4B4;
  207. ifX1R5G5B5: Result := D3DFMT_X1R5G5B5;
  208. ifX4R4G4B4: Result := D3DFMT_X4R4G4B4;
  209. ifR8G8B8: Result := D3DFMT_R8G8B8;
  210. ifA8R8G8B8: Result := D3DFMT_A8R8G8B8;
  211. ifX8R8G8B8: Result := D3DFMT_X8R8G8B8;
  212. ifR16G16B16,
  213. ifA16R16G16B16,
  214. ifB16G16R16:
  215. begin
  216. Result := D3DFMT_A16B16G16R16;
  217. ConversionTo := ifA16B16G16R16;
  218. end;
  219. ifA16B16G16R16: Result := D3DFMT_A16B16G16R16;
  220. ifR32F: Result := D3DFMT_R32F;
  221. ifA32B32G32R32F: Result := D3DFMT_A32B32G32R32F;
  222. ifA32R32G32B32F:
  223. begin
  224. Result := D3DFMT_A32B32G32R32F;
  225. ConversionTo := ifA32B32G32R32F;
  226. end;
  227. ifR16F: Result := D3DFMT_R16F;
  228. ifA16B16G16R16F: Result := D3DFMT_A16B16G16R16F;
  229. ifA16R16G16B16F:
  230. begin
  231. Result := D3DFMT_A16B16G16R16F;
  232. ConversionTo := ifA16B16G16R16F;
  233. end;
  234. ifDXT1: Result := D3DFMT_DXT1;
  235. ifDXT3: Result := D3DFMT_DXT3;
  236. ifDXT5: Result := D3DFMT_DXT5;
  237. ifATI1N: Result := D3DFMT_ATI1;
  238. ifATI2N: Result := D3DFMT_ATI2;
  239. end;
  240. end;
  241. function D3DFormatToImageFormat(Format: TD3DFormat): TImageFormat;
  242. begin
  243. Result := ifUnknown;
  244. case Format of
  245. D3DFMT_P8: Result := ifIndex8;
  246. D3DFMT_A8,
  247. D3DFMT_L8: Result := ifGray8;
  248. D3DFMT_A8L8,
  249. D3DFMT_V8U8: Result := ifA8Gray8;
  250. D3DFMT_L16: Result := ifGray16;
  251. D3DFMT_R3G3B2: Result := ifR3G3B2;
  252. D3DFMT_R5G6B5: Result := ifR5G6B5;
  253. D3DFMT_X1R5G5B5: Result := ifX1R5G5B5;
  254. D3DFMT_A1R5G5B5: Result := ifA1R5G5B5;
  255. D3DFMT_A4R4G4B4: Result := ifA4R4G4B4;
  256. D3DFMT_X4R4G4B4: Result := ifX4R4G4B4;
  257. D3DFMT_R8G8B8: Result := ifR8G8B8;
  258. D3DFMT_A8R8G8B8,
  259. D3DFMT_Q8W8V8U8,
  260. D3DFMT_A8B8G8R8: Result := ifA8R8G8B8;
  261. D3DFMT_X8R8G8B8,
  262. D3DFMT_X8L8V8U8,
  263. D3DFMT_X8B8G8R8: Result := ifX8R8G8B8;
  264. D3DFMT_A16B16G16R16,
  265. D3DFMT_Q16W16V16U16: Result := ifA16B16G16R16;
  266. D3DFMT_R32F: Result := ifR32F;
  267. D3DFMT_A32B32G32R32F: Result := ifA32B32G32R32F;
  268. D3DFMT_R16F: Result := ifR16F;
  269. D3DFMT_A16B16G16R16F: Result := ifA16B16G16R16F;
  270. D3DFMT_DXT1: Result := ifDXT1;
  271. D3DFMT_DXT3: Result := ifDXT3;
  272. D3DFMT_DXT5: Result := ifDXT5;
  273. D3DFMT_ATI1: Result := ifATI1N;
  274. D3DFMT_ATI2: Result := ifATI2N;
  275. end;
  276. end;
  277. function LoadD3DTextureFromFile(const FileName: string; Device: IDirect3DDevice9;
  278. var Texture: IDirect3DTexture9; CreatedWidth, CreatedHeight: PLongInt): Boolean;
  279. var
  280. Images: TDynImageDataArray;
  281. begin
  282. if LoadMultiImageFromFile(FileName, Images) and (Length(Images) > 0) then
  283. begin
  284. Result := CreateD3DTextureFromMultiImage(Images, Device, Texture,
  285. Images[0].Width, Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN,
  286. DefaultPool, CreatedWidth, CreatedHeight);
  287. end
  288. else
  289. Result := False;
  290. FreeImagesInArray(Images);
  291. end;
  292. function LoadD3DTextureFromStream(Stream: TStream; Device: IDirect3DDevice9;
  293. var Texture: IDirect3DTexture9; CreatedWidth, CreatedHeight: PLongInt): Boolean;
  294. var
  295. Images: TDynImageDataArray;
  296. begin
  297. if LoadMultiImageFromStream(Stream, Images) and (Length(Images) > 0) then
  298. begin
  299. Result := CreateD3DTextureFromMultiImage(Images, Device, Texture,
  300. Images[0].Width, Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN,
  301. DefaultPool, CreatedWidth, CreatedHeight);
  302. end
  303. else
  304. Result := False;
  305. FreeImagesInArray(Images);
  306. end;
  307. function LoadD3DTextureFromMemory(Data: Pointer; Size: LongInt;
  308. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9;
  309. CreatedWidth, CreatedHeight: PLongInt): Boolean;
  310. var
  311. Images: TDynImageDataArray;
  312. begin
  313. if LoadMultiImageFromMemory(Data, Size, Images) and (Length(Images) > 0) then
  314. begin
  315. Result := CreateD3DTextureFromMultiImage(Images, Device, Texture, Images[0].Width,
  316. Images[0].Height, 0, DefaultUsage, D3DFMT_UNKNOWN, DefaultPool,
  317. CreatedWidth, CreatedHeight);
  318. end
  319. else
  320. Result := False;
  321. FreeImagesInArray(Images);
  322. end;
  323. function CreateD3DTextureFromImage(const Image: TImageData;
  324. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width,
  325. Height, MipLevels: LongInt; Usage: LongWord; Format: TD3DFormat;
  326. Pool: TD3DPool; CreatedWidth, CreatedHeight: PLongInt): Boolean;
  327. var
  328. Arr: TDynImageDataArray;
  329. begin
  330. // Just calls function operating on image arrays
  331. SetLength(Arr, 1);
  332. Arr[0] := Image;
  333. Result := CreateD3DTextureFromMultiImage(Arr, Device, Texture, Width, Height,
  334. MipLevels, Usage, Format, Pool, CreatedWidth, CreatedHeight);
  335. end;
  336. procedure FillLockedRectWithImage(var Rect: TD3DLockedRect; const Image: TImageData);
  337. var
  338. I, LineBytes: LongInt;
  339. Info: TImageFormatInfo;
  340. begin
  341. GetImageFormatInfo(Image.Format, Info);
  342. LineBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  343. // Pixels of the image are copied to D3D texture
  344. if (not Info.IsSpecial) and (LineBytes < Rect.Pitch) then
  345. begin
  346. for I := 0 to Image.Height - 1 do
  347. Move(PByteArray(Image.Bits)[I * LineBytes],
  348. PByteArray(Rect.pBits)[I * Rect.Pitch], LineBytes);
  349. end
  350. else
  351. Move(Image.Bits^, Rect.pBits^, Image.Size);
  352. end;
  353. function CreateD3DTextureFromMultiImage(const Images: TDynImageDataArray;
  354. Device: IDirect3DDevice9; var Texture: IDirect3DTexture9; Width,
  355. Height, MipLevels: LongInt; Usage: LongWord; Format: TD3DFormat;
  356. Pool: TD3DPool; CreatedWidth, CreatedHeight: PLongInt): Boolean;
  357. var
  358. I, PossibleLevels, ExistingLevels, CurrentWidth, CurrentHeight: LongInt;
  359. Caps: TD3DTextureCaps;
  360. Rect: TD3DLockedRect;
  361. ConvTo: TImageFormat;
  362. LevelsArray: TDynImageDataArray;
  363. NeedsResize, NeedsConvert: Boolean;
  364. begin
  365. Texture := nil;
  366. ExistingLevels := 0;
  367. Result := False;
  368. // Get texture caps of the current device and test if there is anything to convert
  369. if GetDeviceTextureCaps(Device, Caps) and (Length(Images) > 0) then
  370. try
  371. // First check desired size and modify it if necessary
  372. if Width <= 0 then Width := Images[0].Width;
  373. if Height <= 0 then Height := Images[0].Height;
  374. if Caps.PowerOfTwo then
  375. begin
  376. // If device supports only power of 2 texture sizes
  377. Width := NextPow2(Width);
  378. Height := NextPow2(Height);
  379. end;
  380. Width := ClampInt(Width, 1, Caps.MaxWidth);
  381. Height := ClampInt(Height, 1, Caps.MaxHeight);
  382. // Get various mipmap level counts and modify
  383. // desired MipLevels if its value is invalid
  384. ExistingLevels := Length(Images);
  385. PossibleLevels := GetNumMipMapLevels(Width, Height);
  386. if (MipLevels < 1) or (MipLevels > PossibleLevels) then
  387. MipLevels := PossibleLevels;
  388. // Now determine which image format will be used
  389. if Format = D3DFMT_UNKNOWN then
  390. begin
  391. // D3D texture format is not explicitly defined so we use
  392. // the current format of the input image
  393. Format := ImageFormatToD3DFormat(Images[0].Format, ConvTo);
  394. // Format is now either D3DFMT_UNKNOWN or some valid format and
  395. // ConvTo contains format to which input image must be converted first
  396. // (if ConvTo and input image's format differ).
  397. // We must also test if returned D3D format is supported by D3D device
  398. if (Format = D3DFMT_UNKNOWN) or not IsD3DFormatSupported(Device, Format) then
  399. begin
  400. Format := D3DFMT_A8R8G8B8;
  401. ConvTo := ifA8R8G8B8;
  402. end;
  403. end
  404. else
  405. begin
  406. // Image format corresponding to desired D3D format is either found
  407. // and image is converted to it (if the image is not in this format already)
  408. // or it is not found (or not supported by hardware) and default format is used
  409. ConvTo := D3DFormatToImageFormat(Format);
  410. if (ConvTo = ifUnknown) or not IsD3DFormatSupported(Device, Format) then
  411. begin
  412. Format := D3DFMT_A8R8G8B8;
  413. ConvTo := ifA8R8G8B8;
  414. end;
  415. end;
  416. // Prepare array for mipmap levels
  417. SetLength(LevelsArray, MipLevels);
  418. CurrentWidth := Width;
  419. CurrentHeight := Height;
  420. for I := 0 to MipLevels - 1 do
  421. begin
  422. // Check if we can use input image array as a source for this mipmap level
  423. if I < ExistingLevels then
  424. begin
  425. // Check if input image for this mipmap level has the right
  426. // size and format
  427. NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight));
  428. NeedsConvert := not (Images[I].Format = ConvTo);
  429. if NeedsResize or NeedsConvert then
  430. begin
  431. // Input image must be resized or converted to different format
  432. // to become valid mipmap level
  433. CloneImage(Images[I], LevelsArray[I]);
  434. if NeedsConvert then
  435. ConvertImage(LevelsArray[I], ConvTo);
  436. if NeedsResize then
  437. ResizeImage(LevelsArray[I], CurrentWidth, CurrentHeight, rfBilinear);
  438. end
  439. else
  440. // Input image can be used without any changes
  441. LevelsArray[I] := Images[I];
  442. end
  443. else
  444. begin
  445. // This mipmap level is not present in the input image array
  446. // so we create a new level
  447. FillMipMapLevel(LevelsArray[I - 1], CurrentWidth, CurrentHeight, LevelsArray[I]);
  448. end;
  449. // Calculate width and height of the next mipmap level
  450. CurrentWidth := ClampInt(CurrentWidth div 2, 1, CurrentWidth);
  451. CurrentHeight := ClampInt(CurrentHeight div 2, 1, CurrentHeight);
  452. end;
  453. // Finally create D3D texture object
  454. if Succeeded(Device.CreateTexture(LevelsArray[0].Width,
  455. LevelsArray[0].Height, MipLevels, Usage, Format, Pool, Texture, nil)) then
  456. begin
  457. // Fill each mipmap level
  458. for I := 0 to MipLevels - 1 do
  459. if Succeeded(Texture.LockRect(I, Rect, nil, 0)) then
  460. begin
  461. FillLockedRectWithImage(Rect, LevelsArray[I]);
  462. Texture.UnlockRect(I);
  463. end;
  464. Result := True;
  465. end;
  466. // If user is interested in width and height of created texture lets
  467. // give him that
  468. if CreatedWidth <> nil then CreatedWidth^ := LevelsArray[0].Width;
  469. if CreatedHeight <> nil then CreatedHeight^ := LevelsArray[0].Height;
  470. finally
  471. // Free local image copies
  472. for I := 0 to Length(LevelsArray) - 1 do
  473. begin
  474. if ((I < ExistingLevels) and (LevelsArray[I].Bits <> Images[I].Bits)) or
  475. (I >= ExistingLevels) then
  476. FreeImage(LevelsArray[I]);
  477. end;
  478. end;
  479. end;
  480. function SaveD3DTextureToFile(const FileName: string; const Texture: IDirect3DTexture9): Boolean;
  481. var
  482. Arr: TDynImageDataArray;
  483. Fmt: TImageFileFormat;
  484. IsDDS: Boolean;
  485. begin
  486. Result := CreateMultiImageFromD3DTexture(Texture, Arr);
  487. if Result then
  488. begin
  489. Fmt := FindImageFileFormatByName(FileName);
  490. if Fmt <> nil then
  491. begin
  492. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  493. if IsDDS then
  494. begin
  495. PushOptions;
  496. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  497. end;
  498. Result := SaveMultiImageToFile(FileName, Arr);
  499. if IsDDS then
  500. PopOptions;
  501. end;
  502. end;
  503. end;
  504. function SaveD3DTextureToStream(const Ext: string; Stream: TStream; const Texture: IDirect3DTexture9): Boolean;
  505. var
  506. Arr: TDynImageDataArray;
  507. Fmt: TImageFileFormat;
  508. IsDDS: Boolean;
  509. begin
  510. Result := CreateMultiImageFromD3DTexture(Texture, Arr);
  511. if Result then
  512. begin
  513. Fmt := FindImageFileFormatByExt(Ext);
  514. if Fmt <> nil then
  515. begin
  516. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  517. if IsDDS then
  518. begin
  519. PushOptions;
  520. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  521. end;
  522. Result := SaveMultiImageToStream(Ext, Stream, Arr);
  523. if IsDDS then
  524. PopOptions;
  525. end;
  526. end;
  527. end;
  528. function SaveD3DTextureToMemory(const Ext: string; Data: Pointer; var Size: LongInt; const Texture: IDirect3DTexture9): Boolean;
  529. var
  530. Arr: TDynImageDataArray;
  531. Fmt: TImageFileFormat;
  532. IsDDS: Boolean;
  533. begin
  534. Result := CreateMultiImageFromD3DTexture(Texture, Arr);
  535. if Result then
  536. begin
  537. Fmt := FindImageFileFormatByExt(Ext);
  538. if Fmt <> nil then
  539. begin
  540. IsDDS := SameText(Fmt.Extensions[0], 'dds');
  541. if IsDDS then
  542. begin
  543. PushOptions;
  544. SetOption(ImagingDDSSaveMipMapCount, Length(Arr));
  545. end;
  546. Result := SaveMultiImageToMemory(Ext, Data, Size, Arr);
  547. if IsDDS then
  548. PopOptions;
  549. end;
  550. end;
  551. end;
  552. function CreateImageFromD3DTexture(const Texture: IDirect3DTexture9;
  553. var Image: TImageData; OverrideFormat: TImageFormat): Boolean;
  554. var
  555. Arr: TDynImageDataArray;
  556. begin
  557. // Just calls function operating on image arrays
  558. FreeImage(Image);
  559. SetLength(Arr, 1);
  560. Result := CreateMultiImageFromD3DTexture(Texture, Arr, 1, OverrideFormat);
  561. Image := Arr[0];
  562. end;
  563. procedure FillImageWithLockedRect(var Image: TImageData; const Rect: TD3DLockedRect);
  564. var
  565. I, LineBytes: LongInt;
  566. Info: TImageFormatInfo;
  567. begin
  568. GetImageFormatInfo(Image.Format, Info);
  569. LineBytes := Info.GetPixelsSize(Info.Format, Image.Width, 1);
  570. // Pixels are copied from D3D texture to the image
  571. if (not Info.IsSpecial) and (LineBytes < Rect.Pitch) then
  572. begin
  573. for I := 0 to Image.Height - 1 do
  574. Move(PByteArray(Rect.pBits)[I * Rect.Pitch],
  575. PByteArray(Image.Bits)[I * LineBytes], LineBytes);
  576. end
  577. else
  578. Move(Rect.pBits^, Image.Bits^, Image.Size);
  579. end;
  580. function CreateMultiImageFromD3DTexture(const Texture: IDirect3DTexture9;
  581. var Images: TDynImageDataArray; MipLevels: LongInt; OverrideFormat: TImageFormat): Boolean;
  582. var
  583. Rect: TD3DLockedRect;
  584. Desc: TD3DSurfaceDesc;
  585. I,ExistingLevels: LongInt;
  586. CurrentFormat: TImageFormat;
  587. Info: TImageFormatInfo;
  588. begin
  589. FreeImagesInArray(Images);
  590. SetLength(Images, 0);
  591. Result := False;
  592. if Texture <> nil then
  593. begin
  594. // Check if desired mipmap level count is valid
  595. ExistingLevels := Texture.GetLevelCount;
  596. if (MipLevels <= 0) or (MipLevels > ExistingLevels) then
  597. MipLevels := ExistingLevels;
  598. Texture.GetLevelDesc(0, Desc);
  599. // Try to find image format compatible with d3d texture's format
  600. CurrentFormat := D3DFormatToImageFormat(Desc.Format);
  601. // Exit if no compatible image format is found
  602. if CurrentFormat = ifUnknown then
  603. Exit;
  604. SetLength(Images, MipLevels);
  605. GetImageFormatInfo(CurrentFormat, Info);
  606. for I := 0 to MipLevels - 1 do
  607. begin
  608. if Failed(Texture.LockRect(I, Rect, nil, D3DLOCK_READONLY)) then Exit;
  609. Texture.GetLevelDesc(I, Desc);
  610. // Create image for the current mipmap level and copy texture data to it
  611. NewImage(Desc.Width, Desc.Height, CurrentFormat, Images[I]);
  612. FillImageWithLockedRect(Images[I], Rect);
  613. // If override format is set each mipmap level is converted to it
  614. if OverrideFormat <> ifUnknown then
  615. ConvertImage(Images[I], OverrideFormat);
  616. Texture.UnlockRect(I);
  617. end;
  618. Result := True;
  619. end;
  620. end;
  621. function CreateD3DSurfaceFromImage(const Image: TImageData; Surface: IDirect3DSurface9): Boolean;
  622. var
  623. ConvTo: TImageFormat;
  624. Desc: TD3DSurfaceDesc;
  625. Rect: TD3DLockedRect;
  626. WorkImage: TImageData;
  627. begin
  628. Result := False;
  629. if (Surface = nil) or not TestImage(Image) then
  630. Exit;
  631. // Get surface's format and find Imaging data format match
  632. Surface.GetDesc(Desc);
  633. ConvTo := D3DFormatToImageFormat(Desc.Format);
  634. // If no Imaging data format was found we must exit
  635. if ConvTo = ifUnknown then
  636. Exit;
  637. if (LongInt(Desc.Width) <> Image.Width) or (LongInt(Desc.Height) <> Image.Height) or
  638. (Image.Format <> ConvTo) then
  639. begin
  640. // Source image has different dimensions or format than dest surface,
  641. // working image is created
  642. InitImage(WorkImage);
  643. NewImage(Desc.Width, Desc.Height, ConvTo, WorkImage);
  644. StretchRect(Image, 0, 0, Image.Width, Image.Height, WorkImage, 0, 0,
  645. WorkImage.Width, WorkImage.Height, rfBilinear);
  646. end
  647. else
  648. WorkImage := Image;
  649. try
  650. // Lock surface and fill it with image
  651. if Succeeded(Surface.LockRect(Rect, nil, 0)) then
  652. begin
  653. FillLockedRectWithImage(Rect, WorkImage);
  654. Surface.UnlockRect;
  655. Result := True;
  656. end;
  657. finally
  658. // Free working image if it is not reference to source image
  659. if WorkImage.Bits <> Image.Bits then
  660. FreeImage(WorkImage);
  661. end;
  662. end;
  663. function CreateImageFromD3DSurface(Surface: IDirect3DSurface9; var Image: TImageData): Boolean;
  664. var
  665. CurrentFormat: TImageFormat;
  666. Desc: TD3DSurfaceDesc;
  667. Rect: TD3DLockedRect;
  668. begin
  669. Result := False;
  670. FreeImage(Image);
  671. if Surface = nil then
  672. Exit;
  673. Surface.GetDesc(Desc);
  674. CurrentFormat := D3DFormatToImageFormat(Desc.Format);
  675. // Exit if no compatible image format is found
  676. if CurrentFormat = ifUnknown then
  677. Exit;
  678. if Succeeded(Surface.LockRect(Rect, nil, D3DLOCK_READONLY)) then
  679. begin
  680. // If surface was successfully locked a new image is created
  681. // and surface's contents are copied to it
  682. NewImage(Desc.Width, Desc.Height, CurrentFormat, Image);
  683. FillImageWithLockedRect(Image, Rect);
  684. Surface.UnlockRect;
  685. Result := True;
  686. end;
  687. end;
  688. {
  689. File Notes:
  690. -- TODOS ----------------------------------------------------
  691. - support for cube and volume maps
  692. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  693. - Added support for 3Dc compressed texture formats.
  694. - Added detection of 3Dc support to texture caps.
  695. -- 0.21 Changes/Bug Fixes -----------------------------------
  696. - Added CreatedWidth and CreatedHeight parameters to most
  697. LoadD3DTextureFromXXX/CreateD3DTextureFromXXX functions.
  698. -- 0.19 Changes/Bug Fixes -----------------------------------
  699. - fixed bug in CreateGLTextureFromMultiImage which caused assert failure
  700. when creating mipmaps (using FillMipMapLevel) for DXTC formats
  701. - added support for 16bit half-float texture formats
  702. -- 0.17 Changes/Bug Fixes -----------------------------------
  703. - D3D surface support - fill surface with image and vice versa
  704. - more texture caps added
  705. - filtered mipmap creation
  706. -- 0.15 Changes/Bug Fixes -----------------------------------
  707. - unit created and initial stuff added
  708. }
  709. end.