ImagingDirect3D9.pas 28 KB

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