2
0

ImagingDirect3D9.pas 30 KB

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