ImagingDirect3D9.pas 30 KB

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