ImagingDds.pas 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129
  1. {
  2. Vampyre Imaging Library
  3. by Marek Mauder
  4. https://github.com/galfar/imaginglib
  5. https://imaginglib.sourceforge.io
  6. - - - - -
  7. This Source Code Form is subject to the terms of the Mozilla Public
  8. License, v. 2.0. If a copy of the MPL was not distributed with this
  9. file, You can obtain one at https://mozilla.org/MPL/2.0.
  10. }
  11. { This unit contains image format loader/saver for DirectDraw Surface images.}
  12. unit ImagingDds;
  13. {$I ImagingOptions.inc}
  14. interface
  15. uses
  16. ImagingTypes, Imaging, ImagingUtility, ImagingFormats;
  17. type
  18. { Class for loading and saving Microsoft DirectDraw surfaces.
  19. It can load/save all D3D formats which have corresponding
  20. TImageFormat. It supports plain textures, cube textures and
  21. volume textures, all of these can have mipmaps. It can also
  22. load some formats which have no exact TImageFormat, but can be easily
  23. converted to one (bump map formats, etc.).
  24. You can get some information about last loaded DDS file by calling
  25. GetOption with ImagingDDSLoadedXXX options and you can set some
  26. saving options by calling SetOption with ImagingDDSSaveXXX or you can
  27. simply use properties of this class.
  28. Note that when saving cube maps and volumes input image array must contain
  29. at least number of images to build cube/volume based on current
  30. Depth and MipMapCount settings.}
  31. TDDSFileFormat = class(TImageFileFormat)
  32. private
  33. FLoadedCubeMap: LongBool;
  34. FLoadedVolume: LongBool;
  35. FLoadedMipMapCount: LongInt;
  36. FLoadedDepth: LongInt;
  37. FSaveCubeMap: LongBool;
  38. FSaveVolume: LongBool;
  39. FSaveMipMapCount: LongInt;
  40. FSaveDepth: LongInt;
  41. procedure ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt;
  42. IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt);
  43. protected
  44. procedure Define; override;
  45. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  46. OnlyFirstLevel: Boolean): Boolean; override;
  47. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  48. Index: LongInt): Boolean; override;
  49. procedure ConvertToSupported(var Image: TImageData;
  50. const Info: TImageFormatInfo); override;
  51. public
  52. function TestFormat(Handle: TImagingHandle): Boolean; override;
  53. procedure CheckOptionsValidity; override;
  54. published
  55. { True if last loaded DDS file was cube map.}
  56. property LoadedCubeMap: LongBool read FLoadedCubeMap write FLoadedCubeMap;
  57. { True if last loaded DDS file was volume texture.}
  58. property LoadedVolume: LongBool read FLoadedVolume write FLoadedVolume;
  59. { Number of mipmap levels of last loaded DDS image.}
  60. property LoadedMipMapCount: LongInt read FLoadedMipMapCount write FLoadedMipMapCount;
  61. { Depth (slices of volume texture or faces of cube map) of last loaded DDS image.}
  62. property LoadedDepth: LongInt read FLoadedDepth write FLoadedDepth;
  63. { True if next DDS file to be saved should be stored as cube map.}
  64. property SaveCubeMap: LongBool read FSaveCubeMap write FSaveCubeMap;
  65. { True if next DDS file to be saved should be stored as volume texture.}
  66. property SaveVolume: LongBool read FSaveVolume write FSaveVolume;
  67. { Sets the number of mipmaps which should be stored in the next saved DDS file.
  68. Only applies to cube maps and volumes, ordinary 2D textures save all
  69. levels present in input.}
  70. property SaveMipMapCount: LongInt read FSaveMipMapCount write FSaveMipMapCount;
  71. { Sets the depth (slices of volume texture or faces of cube map)
  72. of the next saved DDS file.}
  73. property SaveDepth: LongInt read FSaveDepth write FSaveDepth;
  74. end;
  75. const
  76. { DDS related metadata Ids }
  77. { DXGI format of textures stored in DDS files with DX10 extension. Type is
  78. Enum (value corresponding to DXGI_FORMAT enum from DX SDK).}
  79. SMetaDdsDxgiFormat = 'DdsDxgiFormat';
  80. { Number of mipmaps for each main image in DDS file.}
  81. SMetaDdsMipMapCount = 'DdsMipMapCount';
  82. { Texture array size stored in DDS file (DX10 extension).}
  83. SMetaDdsArraySize = 'DdsArraySize';
  84. implementation
  85. const
  86. SDDSFormatName = 'DirectDraw Surface';
  87. SDDSMasks = '*.dds';
  88. DDSSupportedFormats: TImageFormats = [ifR8G8B8, ifA8R8G8B8, ifX8R8G8B8,
  89. ifA1R5G5B5, ifA4R4G4B4, ifX1R5G5B5, ifX4R4G4B4, ifR5G6B5, ifA16B16G16R16,
  90. ifR32F, ifA32B32G32R32F, ifR16F, ifA16B16G16R16F, ifR3G3B2, ifGray8, ifA8Gray8,
  91. ifGray16, ifDXT1, ifDXT3, ifDXT5, ifATI1N, ifATI2N];
  92. const
  93. { Four character codes.}
  94. DDSMagic = UInt32(Byte('D') or (Byte('D') shl 8) or (Byte('S') shl 16) or
  95. (Byte(' ') shl 24));
  96. FOURCC_DXT1 = UInt32(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  97. (Byte('1') shl 24));
  98. FOURCC_DXT3 = UInt32(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  99. (Byte('3') shl 24));
  100. FOURCC_DXT5 = UInt32(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  101. (Byte('5') shl 24));
  102. FOURCC_ATI1 = UInt32(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  103. (Byte('1') shl 24));
  104. FOURCC_ATI2 = UInt32(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  105. (Byte('2') shl 24));
  106. FOURCC_DX10 = UInt32(Byte('D') or (Byte('X') shl 8) or (Byte('1') shl 16) or
  107. (Byte('0') shl 24));
  108. { Some D3DFORMAT values used in DDS files as FourCC value.}
  109. D3DFMT_A16B16G16R16 = 36;
  110. D3DFMT_R32F = 114;
  111. D3DFMT_A32B32G32R32F = 116;
  112. D3DFMT_R16F = 111;
  113. D3DFMT_A16B16G16R16F = 113;
  114. { Constants used by TDDSurfaceDesc2.Flags.}
  115. DDSD_CAPS = $00000001;
  116. DDSD_HEIGHT = $00000002;
  117. DDSD_WIDTH = $00000004;
  118. DDSD_PITCH = $00000008;
  119. DDSD_PIXELFORMAT = $00001000;
  120. DDSD_MIPMAPCOUNT = $00020000;
  121. DDSD_LINEARSIZE = $00080000;
  122. DDSD_DEPTH = $00800000;
  123. { Constants used by TDDSPixelFormat.Flags.}
  124. DDPF_ALPHAPIXELS = $00000001; // used by formats which contain alpha
  125. DDPF_FOURCC = $00000004; // used by DXT and large ARGB formats
  126. DDPF_RGB = $00000040; // used by RGB formats
  127. DDPF_LUMINANCE = $00020000; // used by formats like D3DFMT_L16
  128. DDPF_BUMPLUMINANCE = $00040000; // used by mixed signed-unsigned formats
  129. DDPF_BUMPDUDV = $00080000; // used by signed formats
  130. { Constants used by TDDSCaps.Caps1.}
  131. DDSCAPS_COMPLEX = $00000008;
  132. DDSCAPS_TEXTURE = $00001000;
  133. DDSCAPS_MIPMAP = $00400000;
  134. { Constants used by TDDSCaps.Caps2.}
  135. DDSCAPS2_CUBEMAP = $00000200;
  136. DDSCAPS2_POSITIVEX = $00000400;
  137. DDSCAPS2_NEGATIVEX = $00000800;
  138. DDSCAPS2_POSITIVEY = $00001000;
  139. DDSCAPS2_NEGATIVEY = $00002000;
  140. DDSCAPS2_POSITIVEZ = $00004000;
  141. DDSCAPS2_NEGATIVEZ = $00008000;
  142. DDSCAPS2_VOLUME = $00200000;
  143. { Flags for TDDSurfaceDesc2.Flags used when saving DDS file.}
  144. DDS_SAVE_FLAGS = DDSD_CAPS or DDSD_PIXELFORMAT or DDSD_WIDTH or
  145. DDSD_HEIGHT or DDSD_LINEARSIZE;
  146. type
  147. { Stores the pixel format information.}
  148. TDDPixelFormat = packed record
  149. Size: UInt32; // Size of the structure = 32 bytes
  150. Flags: UInt32; // Flags to indicate valid fields
  151. FourCC: UInt32; // Four-char code for compressed textures (DXT)
  152. BitCount: UInt32; // Bits per pixel if uncomp. usually 16,24 or 32
  153. RedMask: UInt32; // Bit mask for the Red component
  154. GreenMask: UInt32; // Bit mask for the Green component
  155. BlueMask: UInt32; // Bit mask for the Blue component
  156. AlphaMask: UInt32; // Bit mask for the Alpha component
  157. end;
  158. { Specifies capabilities of surface.}
  159. TDDSCaps = packed record
  160. Caps1: UInt32; // Should always include DDSCAPS_TEXTURE
  161. Caps2: UInt32; // For cubic environment maps
  162. Reserved: array[0..1] of UInt32; // Reserved
  163. end;
  164. { Record describing DDS file contents.}
  165. TDDSurfaceDesc2 = packed record
  166. Size: UInt32; // Size of the structure = 124 Bytes
  167. Flags: UInt32; // Flags to indicate valid fields
  168. Height: UInt32; // Height of the main image in pixels
  169. Width: UInt32; // Width of the main image in pixels
  170. PitchOrLinearSize: UInt32; // For uncomp formats number of bytes per
  171. // scanline. For comp it is the size in
  172. // bytes of the main image
  173. Depth: UInt32; // Only for volume text depth of the volume
  174. MipMaps: Int32; // Total number of levels in the mipmap chain
  175. Reserved1: array[0..10] of UInt32; // Reserved
  176. PixelFormat: TDDPixelFormat; // Format of the pixel data
  177. Caps: TDDSCaps; // Capabilities
  178. Reserved2: UInt32; // Reserved
  179. end;
  180. { DDS file header.}
  181. TDDSFileHeader = packed record
  182. Magic: UInt32; // File format magic
  183. Desc: TDDSurfaceDesc2; // Surface description
  184. end;
  185. { Resource types for D3D 10+ }
  186. TD3D10ResourceDimension = (
  187. D3D10_RESOURCE_DIMENSION_UNKNOWN = 0,
  188. D3D10_RESOURCE_DIMENSION_BUFFER = 1,
  189. D3D10_RESOURCE_DIMENSION_TEXTURE1D = 2,
  190. D3D10_RESOURCE_DIMENSION_TEXTURE2D = 3,
  191. D3D10_RESOURCE_DIMENSION_TEXTURE3D = 4
  192. );
  193. { Texture formats for D3D 10+ }
  194. TDXGIFormat = (
  195. DXGI_FORMAT_UNKNOWN = 0,
  196. DXGI_FORMAT_R32G32B32A32_TYPELESS = 1,
  197. DXGI_FORMAT_R32G32B32A32_FLOAT = 2,
  198. DXGI_FORMAT_R32G32B32A32_UINT = 3,
  199. DXGI_FORMAT_R32G32B32A32_SINT = 4,
  200. DXGI_FORMAT_R32G32B32_TYPELESS = 5,
  201. DXGI_FORMAT_R32G32B32_FLOAT = 6,
  202. DXGI_FORMAT_R32G32B32_UINT = 7,
  203. DXGI_FORMAT_R32G32B32_SINT = 8,
  204. DXGI_FORMAT_R16G16B16A16_TYPELESS = 9,
  205. DXGI_FORMAT_R16G16B16A16_FLOAT = 10,
  206. DXGI_FORMAT_R16G16B16A16_UNORM = 11,
  207. DXGI_FORMAT_R16G16B16A16_UINT = 12,
  208. DXGI_FORMAT_R16G16B16A16_SNORM = 13,
  209. DXGI_FORMAT_R16G16B16A16_SINT = 14,
  210. DXGI_FORMAT_R32G32_TYPELESS = 15,
  211. DXGI_FORMAT_R32G32_FLOAT = 16,
  212. DXGI_FORMAT_R32G32_UINT = 17,
  213. DXGI_FORMAT_R32G32_SINT = 18,
  214. DXGI_FORMAT_R32G8X24_TYPELESS = 19,
  215. DXGI_FORMAT_D32_FLOAT_S8X24_UINT = 20,
  216. DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS = 21,
  217. DXGI_FORMAT_X32_TYPELESS_G8X24_UINT = 22,
  218. DXGI_FORMAT_R10G10B10A2_TYPELESS = 23,
  219. DXGI_FORMAT_R10G10B10A2_UNORM = 24,
  220. DXGI_FORMAT_R10G10B10A2_UINT = 25,
  221. DXGI_FORMAT_R11G11B10_FLOAT = 26,
  222. DXGI_FORMAT_R8G8B8A8_TYPELESS = 27,
  223. DXGI_FORMAT_R8G8B8A8_UNORM = 28,
  224. DXGI_FORMAT_R8G8B8A8_UNORM_SRGB = 29,
  225. DXGI_FORMAT_R8G8B8A8_UINT = 30,
  226. DXGI_FORMAT_R8G8B8A8_SNORM = 31,
  227. DXGI_FORMAT_R8G8B8A8_SINT = 32,
  228. DXGI_FORMAT_R16G16_TYPELESS = 33,
  229. DXGI_FORMAT_R16G16_FLOAT = 34,
  230. DXGI_FORMAT_R16G16_UNORM = 35,
  231. DXGI_FORMAT_R16G16_UINT = 36,
  232. DXGI_FORMAT_R16G16_SNORM = 37,
  233. DXGI_FORMAT_R16G16_SINT = 38,
  234. DXGI_FORMAT_R32_TYPELESS = 39,
  235. DXGI_FORMAT_D32_FLOAT = 40,
  236. DXGI_FORMAT_R32_FLOAT = 41,
  237. DXGI_FORMAT_R32_UINT = 42,
  238. DXGI_FORMAT_R32_SINT = 43,
  239. DXGI_FORMAT_R24G8_TYPELESS = 44,
  240. DXGI_FORMAT_D24_UNORM_S8_UINT = 45,
  241. DXGI_FORMAT_R24_UNORM_X8_TYPELESS = 46,
  242. DXGI_FORMAT_X24_TYPELESS_G8_UINT = 47,
  243. DXGI_FORMAT_R8G8_TYPELESS = 48,
  244. DXGI_FORMAT_R8G8_UNORM = 49,
  245. DXGI_FORMAT_R8G8_UINT = 50,
  246. DXGI_FORMAT_R8G8_SNORM = 51,
  247. DXGI_FORMAT_R8G8_SINT = 52,
  248. DXGI_FORMAT_R16_TYPELESS = 53,
  249. DXGI_FORMAT_R16_FLOAT = 54,
  250. DXGI_FORMAT_D16_UNORM = 55,
  251. DXGI_FORMAT_R16_UNORM = 56,
  252. DXGI_FORMAT_R16_UINT = 57,
  253. DXGI_FORMAT_R16_SNORM = 58,
  254. DXGI_FORMAT_R16_SINT = 59,
  255. DXGI_FORMAT_R8_TYPELESS = 60,
  256. DXGI_FORMAT_R8_UNORM = 61,
  257. DXGI_FORMAT_R8_UINT = 62,
  258. DXGI_FORMAT_R8_SNORM = 63,
  259. DXGI_FORMAT_R8_SINT = 64,
  260. DXGI_FORMAT_A8_UNORM = 65,
  261. DXGI_FORMAT_R1_UNORM = 66,
  262. DXGI_FORMAT_R9G9B9E5_SHAREDEXP = 67,
  263. DXGI_FORMAT_R8G8_B8G8_UNORM = 68,
  264. DXGI_FORMAT_G8R8_G8B8_UNORM = 69,
  265. DXGI_FORMAT_BC1_TYPELESS = 70,
  266. DXGI_FORMAT_BC1_UNORM = 71,
  267. DXGI_FORMAT_BC1_UNORM_SRGB = 72,
  268. DXGI_FORMAT_BC2_TYPELESS = 73,
  269. DXGI_FORMAT_BC2_UNORM = 74,
  270. DXGI_FORMAT_BC2_UNORM_SRGB = 75,
  271. DXGI_FORMAT_BC3_TYPELESS = 76,
  272. DXGI_FORMAT_BC3_UNORM = 77,
  273. DXGI_FORMAT_BC3_UNORM_SRGB = 78,
  274. DXGI_FORMAT_BC4_TYPELESS = 79,
  275. DXGI_FORMAT_BC4_UNORM = 80,
  276. DXGI_FORMAT_BC4_SNORM = 81,
  277. DXGI_FORMAT_BC5_TYPELESS = 82,
  278. DXGI_FORMAT_BC5_UNORM = 83,
  279. DXGI_FORMAT_BC5_SNORM = 84,
  280. DXGI_FORMAT_B5G6R5_UNORM = 85,
  281. DXGI_FORMAT_B5G5R5A1_UNORM = 86,
  282. DXGI_FORMAT_B8G8R8A8_UNORM = 87,
  283. DXGI_FORMAT_B8G8R8X8_UNORM = 88,
  284. DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM = 89,
  285. DXGI_FORMAT_B8G8R8A8_TYPELESS = 90,
  286. DXGI_FORMAT_B8G8R8A8_UNORM_SRGB = 91,
  287. DXGI_FORMAT_B8G8R8X8_TYPELESS = 92,
  288. DXGI_FORMAT_B8G8R8X8_UNORM_SRGB = 93,
  289. DXGI_FORMAT_BC6H_TYPELESS = 94,
  290. DXGI_FORMAT_BC6H_UF16 = 95,
  291. DXGI_FORMAT_BC6H_SF16 = 96,
  292. DXGI_FORMAT_BC7_TYPELESS = 97,
  293. DXGI_FORMAT_BC7_UNORM = 98,
  294. DXGI_FORMAT_BC7_UNORM_SRGB = 99,
  295. DXGI_FORMAT_AYUV = 100,
  296. DXGI_FORMAT_Y410 = 101,
  297. DXGI_FORMAT_Y416 = 102,
  298. DXGI_FORMAT_NV12 = 103,
  299. DXGI_FORMAT_P010 = 104,
  300. DXGI_FORMAT_P016 = 105,
  301. DXGI_FORMAT_420_OPAQUE = 106,
  302. DXGI_FORMAT_YUY2 = 107,
  303. DXGI_FORMAT_Y210 = 108,
  304. DXGI_FORMAT_Y216 = 109,
  305. DXGI_FORMAT_NV11 = 110,
  306. DXGI_FORMAT_AI44 = 111,
  307. DXGI_FORMAT_IA44 = 112,
  308. DXGI_FORMAT_P8 = 113,
  309. DXGI_FORMAT_A8P8 = 114,
  310. DXGI_FORMAT_B4G4R4A4_UNORM = 115
  311. );
  312. { DX10 extension header for DDS file format }
  313. TDX10Header = packed record
  314. DXGIFormat: TDXGIFormat;
  315. ResourceDimension: TD3D10ResourceDimension;
  316. MiscFlags: UInt32;
  317. ArraySize: UInt32;
  318. Reserved: UInt32;
  319. end;
  320. { TDDSFileFormat class implementation }
  321. procedure TDDSFileFormat.Define;
  322. begin
  323. inherited;
  324. FName := SDDSFormatName;
  325. FFeatures := [ffLoad, ffSave, ffMultiImage];
  326. FSupportedFormats := DDSSupportedFormats;
  327. FSaveCubeMap := False;
  328. FSaveVolume := False;
  329. FSaveMipMapCount := 1;
  330. FSaveDepth := 1;
  331. AddMasks(SDDSMasks);
  332. RegisterOption(ImagingDDSLoadedCubeMap, @FLoadedCubeMap);
  333. RegisterOption(ImagingDDSLoadedVolume, @FLoadedVolume);
  334. RegisterOption(ImagingDDSLoadedMipMapCount, @FLoadedMipMapCount);
  335. RegisterOption(ImagingDDSLoadedDepth, @FLoadedDepth);
  336. RegisterOption(ImagingDDSSaveCubeMap, @FSaveCubeMap);
  337. RegisterOption(ImagingDDSSaveVolume, @FSaveVolume);
  338. RegisterOption(ImagingDDSSaveMipMapCount, @FSaveMipMapCount);
  339. RegisterOption(ImagingDDSSaveDepth, @FSaveDepth);
  340. end;
  341. procedure TDDSFileFormat.CheckOptionsValidity;
  342. begin
  343. if FSaveCubeMap then
  344. FSaveVolume := False;
  345. if FSaveVolume then
  346. FSaveCubeMap := False;
  347. if FSaveDepth < 1 then
  348. FSaveDepth := 1;
  349. if FSaveMipMapCount < 1 then
  350. FSaveMipMapCount := 1;
  351. end;
  352. procedure TDDSFileFormat.ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt;
  353. IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt);
  354. var
  355. I, Last, Shift: LongInt;
  356. begin
  357. CurWidth := Width;
  358. CurHeight := Height;
  359. if MipMaps > 1 then
  360. begin
  361. if not IsVolume then
  362. begin
  363. if IsCubeMap then
  364. begin
  365. // Cube maps are stored like this
  366. // Face 0 mipmap 0
  367. // Face 0 mipmap 1
  368. // ...
  369. // Face 1 mipmap 0
  370. // Face 1 mipmap 1
  371. // ...
  372. // Modify index so later in for loop we iterate less times
  373. Idx := Idx - ((Idx div MipMaps) * MipMaps);
  374. end;
  375. for I := 0 to Idx - 1 do
  376. begin
  377. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  378. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  379. end;
  380. end
  381. else
  382. begin
  383. // Volume textures are stored in DDS files like this:
  384. // Slice 0 mipmap 0
  385. // Slice 1 mipmap 0
  386. // Slice 2 mipmap 0
  387. // Slice 3 mipmap 0
  388. // Slice 0 mipmap 1
  389. // Slice 1 mipmap 1
  390. // Slice 0 mipmap 2
  391. // Slice 0 mipmap 3 ...
  392. Shift := 0;
  393. Last := Depth;
  394. while Idx > Last - 1 do
  395. begin
  396. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  397. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  398. if (CurWidth = 1) and (CurHeight = 1) then
  399. Break;
  400. Inc(Shift);
  401. Inc(Last, ClampInt(Depth shr Shift, 1, Depth));
  402. end;
  403. end;
  404. end;
  405. end;
  406. function TDDSFileFormat.LoadData(Handle: TImagingHandle;
  407. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  408. var
  409. Hdr: TDDSFileHeader;
  410. HdrDX10: TDX10Header;
  411. SrcFormat: TImageFormat;
  412. FmtInfo: TImageFormatInfo;
  413. NeedsSwapChannels: Boolean;
  414. CurrentWidth, CurrentHeight, ImageCount, LoadSize, I,
  415. PitchOrLinear, MainImageLinearSize: LongInt;
  416. Data: PByte;
  417. UseAsPitch: Boolean;
  418. UseAsLinear: Boolean;
  419. function MasksEqual(const DDPF: TDDPixelFormat; PF: PPixelFormatInfo): Boolean;
  420. begin
  421. Result := (DDPF.AlphaMask = PF.ABitMask) and
  422. (DDPF.RedMask = PF.RBitMask) and (DDPF.GreenMask = PF.GBitMask) and
  423. (DDPF.BlueMask = PF.BBitMask);
  424. end;
  425. function FindFourCCFormat(FourCC: UInt32): TImageFormat;
  426. begin
  427. // Handle FourCC and large ARGB formats
  428. case FourCC of
  429. D3DFMT_A16B16G16R16: Result := ifA16B16G16R16;
  430. D3DFMT_R32F: Result := ifR32F;
  431. D3DFMT_A32B32G32R32F: Result := ifA32B32G32R32F;
  432. D3DFMT_R16F: Result := ifR16F;
  433. D3DFMT_A16B16G16R16F: Result := ifA16B16G16R16F;
  434. FOURCC_DXT1: Result := ifDXT1;
  435. FOURCC_DXT3: Result := ifDXT3;
  436. FOURCC_DXT5: Result := ifDXT5;
  437. FOURCC_ATI1: Result := ifATI1N;
  438. FOURCC_ATI2: Result := ifATI2N;
  439. else
  440. Result := ifUnknown;
  441. end;
  442. end;
  443. function FindDX10Format(DXGIFormat: TDXGIFormat; var NeedsSwapChannels: Boolean): TImageFormat;
  444. begin
  445. Result := ifUnknown;
  446. NeedsSwapChannels := False;
  447. case DXGIFormat of
  448. DXGI_FORMAT_UNKNOWN: ;
  449. DXGI_FORMAT_R32G32B32A32_TYPELESS, DXGI_FORMAT_R32G32B32A32_FLOAT:
  450. Result := ifA32B32G32R32F;
  451. DXGI_FORMAT_R32G32B32A32_UINT: ;
  452. DXGI_FORMAT_R32G32B32A32_SINT: ;
  453. DXGI_FORMAT_R32G32B32_TYPELESS, DXGI_FORMAT_R32G32B32_FLOAT:
  454. Result := ifB32G32R32F;
  455. DXGI_FORMAT_R32G32B32_UINT: ;
  456. DXGI_FORMAT_R32G32B32_SINT: ;
  457. DXGI_FORMAT_R16G16B16A16_FLOAT:
  458. Result := ifA16B16G16R16F;
  459. DXGI_FORMAT_R16G16B16A16_TYPELESS, DXGI_FORMAT_R16G16B16A16_UNORM,
  460. DXGI_FORMAT_R16G16B16A16_UINT, DXGI_FORMAT_R16G16B16A16_SNORM,
  461. DXGI_FORMAT_R16G16B16A16_SINT:
  462. Result := ifA16B16G16R16;
  463. DXGI_FORMAT_R32G32_TYPELESS: ;
  464. DXGI_FORMAT_R32G32_FLOAT: ;
  465. DXGI_FORMAT_R32G32_UINT: ;
  466. DXGI_FORMAT_R32G32_SINT: ;
  467. DXGI_FORMAT_R32G8X24_TYPELESS: ;
  468. DXGI_FORMAT_D32_FLOAT_S8X24_UINT: ;
  469. DXGI_FORMAT_R32_FLOAT_X8X24_TYPELESS: ;
  470. DXGI_FORMAT_X32_TYPELESS_G8X24_UINT: ;
  471. DXGI_FORMAT_R10G10B10A2_TYPELESS: ;
  472. DXGI_FORMAT_R10G10B10A2_UNORM: ;
  473. DXGI_FORMAT_R10G10B10A2_UINT: ;
  474. DXGI_FORMAT_R11G11B10_FLOAT: ;
  475. DXGI_FORMAT_R8G8B8A8_TYPELESS, DXGI_FORMAT_R8G8B8A8_UNORM,
  476. DXGI_FORMAT_R8G8B8A8_UINT, DXGI_FORMAT_R8G8B8A8_SNORM,DXGI_FORMAT_R8G8B8A8_SINT,
  477. DXGI_FORMAT_R8G8B8A8_UNORM_SRGB:
  478. begin
  479. Result := ifA8R8G8B8;
  480. NeedsSwapChannels := True;
  481. end;
  482. DXGI_FORMAT_R16G16_TYPELESS: ;
  483. DXGI_FORMAT_R16G16_FLOAT: ;
  484. DXGI_FORMAT_R16G16_UNORM: ;
  485. DXGI_FORMAT_R16G16_UINT: ;
  486. DXGI_FORMAT_R16G16_SNORM: ;
  487. DXGI_FORMAT_R16G16_SINT: ;
  488. DXGI_FORMAT_R32_TYPELESS, DXGI_FORMAT_R32_UINT, DXGI_FORMAT_R32_SINT:
  489. Result := ifGray32;
  490. DXGI_FORMAT_D32_FLOAT, DXGI_FORMAT_R32_FLOAT:
  491. Result := ifR32F;
  492. DXGI_FORMAT_R24G8_TYPELESS: ;
  493. DXGI_FORMAT_D24_UNORM_S8_UINT: ;
  494. DXGI_FORMAT_R24_UNORM_X8_TYPELESS: ;
  495. DXGI_FORMAT_X24_TYPELESS_G8_UINT: ;
  496. DXGI_FORMAT_R8G8_TYPELESS, DXGI_FORMAT_R8G8_UNORM, DXGI_FORMAT_R8G8_UINT,
  497. DXGI_FORMAT_R8G8_SNORM, DXGI_FORMAT_R8G8_SINT:
  498. Result := ifA8Gray8;
  499. DXGI_FORMAT_R16_TYPELESS, DXGI_FORMAT_D16_UNORM, DXGI_FORMAT_R16_UNORM,
  500. DXGI_FORMAT_R16_UINT, DXGI_FORMAT_R16_SNORM, DXGI_FORMAT_R16_SINT:
  501. Result := ifGray16;
  502. DXGI_FORMAT_R16_FLOAT:
  503. Result := ifR16F;
  504. DXGI_FORMAT_R8_TYPELESS, DXGI_FORMAT_R8_UNORM, DXGI_FORMAT_R8_UINT,
  505. DXGI_FORMAT_R8_SNORM, DXGI_FORMAT_R8_SINT, DXGI_FORMAT_A8_UNORM:
  506. Result := ifGray8;
  507. DXGI_FORMAT_R1_UNORM: ;
  508. DXGI_FORMAT_R9G9B9E5_SHAREDEXP: ;
  509. DXGI_FORMAT_R8G8_B8G8_UNORM: ;
  510. DXGI_FORMAT_G8R8_G8B8_UNORM: ;
  511. DXGI_FORMAT_BC1_TYPELESS, DXGI_FORMAT_BC1_UNORM, DXGI_FORMAT_BC1_UNORM_SRGB:
  512. Result := ifDXT1;
  513. DXGI_FORMAT_BC2_TYPELESS, DXGI_FORMAT_BC2_UNORM, DXGI_FORMAT_BC2_UNORM_SRGB:
  514. Result := ifDXT3;
  515. DXGI_FORMAT_BC3_TYPELESS, DXGI_FORMAT_BC3_UNORM, DXGI_FORMAT_BC3_UNORM_SRGB:
  516. Result := ifDXT5;
  517. DXGI_FORMAT_BC4_TYPELESS, DXGI_FORMAT_BC4_UNORM, DXGI_FORMAT_BC4_SNORM:
  518. Result := ifATI1N;
  519. DXGI_FORMAT_BC5_TYPELESS, DXGI_FORMAT_BC5_UNORM, DXGI_FORMAT_BC5_SNORM:
  520. Result := ifATI2N;
  521. DXGI_FORMAT_B5G6R5_UNORM:
  522. Result := ifR5G6B5;
  523. DXGI_FORMAT_B5G5R5A1_UNORM:
  524. Result := ifA1R5G5B5;
  525. DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_B8G8R8A8_TYPELESS:
  526. Result := ifA8R8G8B8;
  527. DXGI_FORMAT_B8G8R8X8_UNORM, DXGI_FORMAT_B8G8R8X8_TYPELESS:
  528. Result := ifX8R8G8B8;
  529. DXGI_FORMAT_R10G10B10_XR_BIAS_A2_UNORM: ;
  530. DXGI_FORMAT_B8G8R8A8_UNORM_SRGB: ;
  531. DXGI_FORMAT_B8G8R8X8_UNORM_SRGB: ;
  532. DXGI_FORMAT_BC6H_TYPELESS: ;
  533. DXGI_FORMAT_BC6H_UF16: ;
  534. DXGI_FORMAT_BC6H_SF16: ;
  535. DXGI_FORMAT_BC7_TYPELESS: ;
  536. DXGI_FORMAT_BC7_UNORM: ;
  537. DXGI_FORMAT_BC7_UNORM_SRGB: ;
  538. DXGI_FORMAT_P8: ;
  539. DXGI_FORMAT_A8P8: ;
  540. DXGI_FORMAT_B4G4R4A4_UNORM:
  541. Result := ifA4R4G4B4;
  542. end;
  543. end;
  544. begin
  545. Result := False;
  546. ImageCount := 1;
  547. FLoadedMipMapCount := 1;
  548. FLoadedDepth := 1;
  549. FLoadedVolume := False;
  550. FLoadedCubeMap := False;
  551. ZeroMemory(@HdrDX10, SizeOf(HdrDX10));
  552. with GetIO, Hdr, Hdr.Desc.PixelFormat do
  553. begin
  554. Read(Handle, @Hdr, SizeOf(Hdr));
  555. SrcFormat := ifUnknown;
  556. NeedsSwapChannels := False;
  557. // Get image data format
  558. if (Flags and DDPF_FOURCC) = DDPF_FOURCC then
  559. begin
  560. if FourCC = FOURCC_DX10 then
  561. begin
  562. Read(Handle, @HdrDX10, SizeOf(HdrDX10));
  563. SrcFormat := FindDX10Format(HdrDX10.DXGIFormat, NeedsSwapChannels);
  564. FMetadata.SetMetaItem(SMetaDdsDxgiFormat, HdrDX10.DXGIFormat);
  565. FMetadata.SetMetaItem(SMetaDdsArraySize, HdrDX10.ArraySize);
  566. end
  567. else
  568. SrcFormat := FindFourCCFormat(FourCC);
  569. end
  570. else if (Flags and DDPF_RGB) = DDPF_RGB then
  571. begin
  572. // Handle RGB formats
  573. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  574. begin
  575. // Handle RGB with alpha formats
  576. case BitCount of
  577. 16:
  578. begin
  579. if MasksEqual(Desc.PixelFormat, GetFormatInfo(ifA4R4G4B4).PixelFormat) then
  580. SrcFormat := ifA4R4G4B4;
  581. if MasksEqual(Desc.PixelFormat, GetFormatInfo(ifA1R5G5B5).PixelFormat) then
  582. SrcFormat := ifA1R5G5B5;
  583. end;
  584. 32:
  585. begin
  586. SrcFormat := ifA8R8G8B8;
  587. if BlueMask = $00FF0000 then
  588. NeedsSwapChannels := True;
  589. end;
  590. end;
  591. end
  592. else
  593. begin
  594. // Handle RGB without alpha formats
  595. case BitCount of
  596. 8:
  597. if MasksEqual(Desc.PixelFormat,
  598. GetFormatInfo(ifR3G3B2).PixelFormat) then
  599. SrcFormat := ifR3G3B2;
  600. 16:
  601. begin
  602. if MasksEqual(Desc.PixelFormat,
  603. GetFormatInfo(ifX4R4G4B4).PixelFormat) then
  604. SrcFormat := ifX4R4G4B4;
  605. if MasksEqual(Desc.PixelFormat,
  606. GetFormatInfo(ifX1R5G5B5).PixelFormat) then
  607. SrcFormat := ifX1R5G5B5;
  608. if MasksEqual(Desc.PixelFormat,
  609. GetFormatInfo(ifR5G6B5).PixelFormat) then
  610. SrcFormat := ifR5G6B5;
  611. end;
  612. 24: SrcFormat := ifR8G8B8;
  613. 32:
  614. begin
  615. SrcFormat := ifX8R8G8B8;
  616. if BlueMask = $00FF0000 then
  617. NeedsSwapChannels := True;
  618. end;
  619. end;
  620. end;
  621. end
  622. else if (Flags and DDPF_LUMINANCE) = DDPF_LUMINANCE then
  623. begin
  624. // Handle luminance formats
  625. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  626. begin
  627. // Handle luminance with alpha formats
  628. if BitCount = 16 then
  629. SrcFormat := ifA8Gray8;
  630. end
  631. else
  632. begin
  633. // Handle luminance without alpha formats
  634. case BitCount of
  635. 8: SrcFormat := ifGray8;
  636. 16: SrcFormat := ifGray16;
  637. end;
  638. end;
  639. end
  640. else if (Flags and DDPF_BUMPLUMINANCE) = DDPF_BUMPLUMINANCE then
  641. begin
  642. // Handle mixed bump-luminance formats like D3DFMT_X8L8V8U8
  643. case BitCount of
  644. 32:
  645. if BlueMask = $00FF0000 then
  646. begin
  647. SrcFormat := ifX8R8G8B8; // D3DFMT_X8L8V8U8
  648. NeedsSwapChannels := True;
  649. end;
  650. end;
  651. end
  652. else if (Flags and DDPF_BUMPDUDV) = DDPF_BUMPDUDV then
  653. begin
  654. // Handle bumpmap formats like D3DFMT_Q8W8V8U8
  655. case BitCount of
  656. 16: SrcFormat := ifA8Gray8; // D3DFMT_V8U8
  657. 32:
  658. if AlphaMask = $FF000000 then
  659. begin
  660. SrcFormat := ifA8R8G8B8; // D3DFMT_Q8W8V8U8
  661. NeedsSwapChannels := True;
  662. end;
  663. 64: SrcFormat := ifA16B16G16R16; // D3DFMT_Q16W16V16U16
  664. end;
  665. end;
  666. // If DDS format is not supported we will exit
  667. if SrcFormat = ifUnknown then
  668. Exit;
  669. // File contains mipmaps for each subimage.
  670. { Some DDS writers ignore setting proper Caps and Flags so
  671. this check is not usable:
  672. if ((Desc.Caps.Caps1 and DDSCAPS_MIPMAP) = DDSCAPS_MIPMAP) and
  673. ((Desc.Flags and DDSD_MIPMAPCOUNT) = DDSD_MIPMAPCOUNT) then}
  674. if Desc.MipMaps > 1 then
  675. begin
  676. FLoadedMipMapCount := Desc.MipMaps;
  677. FMetadata.SetMetaItem(SMetaDdsMipMapCount, Desc.MipMaps);
  678. ImageCount := Desc.MipMaps;
  679. end;
  680. // File stores volume texture
  681. if ((Desc.Caps.Caps2 and DDSCAPS2_VOLUME) = DDSCAPS2_VOLUME) and
  682. ((Desc.Flags and DDSD_DEPTH) = DDSD_DEPTH) then
  683. begin
  684. FLoadedVolume := True;
  685. FLoadedDepth := Desc.Depth;
  686. ImageCount := GetVolumeLevelCount(Desc.Depth, ImageCount);
  687. end;
  688. // File stores cube texture
  689. if (Desc.Caps.Caps2 and DDSCAPS2_CUBEMAP) = DDSCAPS2_CUBEMAP then
  690. begin
  691. FLoadedCubeMap := True;
  692. I := 0;
  693. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEX) = DDSCAPS2_POSITIVEX then Inc(I);
  694. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEY) = DDSCAPS2_POSITIVEY then Inc(I);
  695. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEZ) = DDSCAPS2_POSITIVEZ then Inc(I);
  696. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEX) = DDSCAPS2_NEGATIVEX then Inc(I);
  697. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEY) = DDSCAPS2_NEGATIVEY then Inc(I);
  698. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEZ) = DDSCAPS2_NEGATIVEZ then Inc(I);
  699. FLoadedDepth := I;
  700. ImageCount := ImageCount * I;
  701. end;
  702. // Allocate and load all images in file
  703. FmtInfo := GetFormatInfo(SrcFormat);
  704. SetLength(Images, ImageCount);
  705. // Compute the pitch or get if from file if present
  706. UseAsPitch := (Desc.Flags and DDSD_PITCH) = DDSD_PITCH;
  707. UseAsLinear := (Desc.Flags and DDSD_LINEARSIZE) = DDSD_LINEARSIZE;
  708. // Use linear as default if none is set
  709. if not UseAsPitch and not UseAsLinear then
  710. UseAsLinear := True;
  711. // Main image pitch or linear size
  712. PitchOrLinear := Desc.PitchOrLinearSize;
  713. // Check: some writers just write garbage to pitch/linear size fields and flags
  714. MainImageLinearSize := FmtInfo.GetPixelsSize(SrcFormat, Desc.Width, Desc.Height);
  715. if UseAsLinear and ((PitchOrLinear < MainImageLinearSize) or
  716. (PitchOrLinear * Integer(Desc.Height) = MainImageLinearSize)) then
  717. begin
  718. // Explicitly set linear size
  719. PitchOrLinear := MainImageLinearSize;
  720. end;
  721. for I := 0 to ImageCount - 1 do
  722. begin
  723. // Compute dimensions of surrent subimage based on texture type and
  724. // number of mipmaps
  725. ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth,
  726. FLoadedCubeMap, FLoadedVolume, CurrentWidth, CurrentHeight);
  727. NewImage(CurrentWidth, CurrentHeight, SrcFormat, Images[I]);
  728. if (I > 0) or (PitchOrLinear = 0) then
  729. begin
  730. // Compute pitch or linear size for mipmap levels, or even for main image
  731. // since some formats do not fill pitch nor size
  732. if UseAsLinear then
  733. PitchOrLinear := FmtInfo.GetPixelsSize(SrcFormat, CurrentWidth, CurrentHeight)
  734. else
  735. PitchOrLinear := (CurrentWidth * FmtInfo.BytesPerPixel + 3) div 4 * 4; // must be DWORD aligned
  736. end;
  737. if UseAsLinear then
  738. LoadSize := PitchOrLinear
  739. else
  740. LoadSize := CurrentHeight * PitchOrLinear;
  741. if UseAsLinear or (LoadSize = Images[I].Size) then
  742. begin
  743. // If DDS does not use Pitch we can simply copy data
  744. Read(Handle, Images[I].Bits, LoadSize)
  745. end
  746. else
  747. begin
  748. // If DDS uses Pitch we must load aligned scanlines
  749. // and then remove padding
  750. GetMem(Data, LoadSize);
  751. try
  752. Read(Handle, Data, LoadSize);
  753. RemovePadBytes(Data, Images[I].Bits, CurrentWidth, CurrentHeight,
  754. FmtInfo.BytesPerPixel, PitchOrLinear);
  755. finally
  756. FreeMem(Data);
  757. end;
  758. end;
  759. if NeedsSwapChannels then
  760. SwapChannels(Images[I], ChannelRed, ChannelBlue);
  761. end;
  762. Result := True;
  763. end;
  764. end;
  765. function TDDSFileFormat.SaveData(Handle: TImagingHandle;
  766. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  767. var
  768. Hdr: TDDSFileHeader;
  769. MainImage, ImageToSave: TImageData;
  770. I, MainIdx, Len, ImageCount: LongInt;
  771. J: UInt32;
  772. FmtInfo: TImageFormatInfo;
  773. MustBeFreed: Boolean;
  774. Is2DTexture, IsCubeMap, IsVolume: Boolean;
  775. MipMapCount, CurrentWidth, CurrentHeight: LongInt;
  776. NeedsResize: Boolean;
  777. NeedsConvert: Boolean;
  778. begin
  779. Result := False;
  780. FillChar(Hdr, Sizeof(Hdr), 0);
  781. MainIdx := FFirstIdx;
  782. Len := FLastIdx - MainIdx + 1;
  783. // Some DDS saving rules:
  784. // 2D textures: Len is used as mipmap count (FSaveMipMapCount not used!).
  785. // Cube maps: FSaveDepth * FSaveMipMapCount images are used, if Len is
  786. // smaller than this file is saved as regular 2D texture.
  787. // Volume maps: GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) images are
  788. // used, if Len is smaller than this file is
  789. // saved as regular 2D texture.
  790. IsCubeMap := FSaveCubeMap;
  791. IsVolume := FSaveVolume;
  792. MipMapCount := FSaveMipMapCount;
  793. if IsCubeMap then
  794. begin
  795. // Check if we have enough images on Input to save cube map
  796. if Len < FSaveDepth * FSaveMipMapCount then
  797. IsCubeMap := False;
  798. end
  799. else if IsVolume then
  800. begin
  801. // Check if we have enough images on Input to save volume texture
  802. if Len < GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) then
  803. IsVolume := False;
  804. end;
  805. Is2DTexture := not IsCubeMap and not IsVolume;
  806. if Is2DTexture then
  807. begin
  808. // Get number of mipmaps used with 2D texture
  809. MipMapCount := Min(Len, GetNumMipMapLevels(Images[MainIdx].Width, Images[MainIdx].Height));
  810. end;
  811. // we create compatible main image and fill headers
  812. if MakeCompatible(Images[MainIdx], MainImage, MustBeFreed) then
  813. with GetIO, MainImage, Hdr do
  814. try
  815. FmtInfo := GetFormatInfo(Format);
  816. Magic := DDSMagic;
  817. Desc.Size := SizeOf(Desc);
  818. Desc.Width := Width;
  819. Desc.Height := Height;
  820. Desc.Flags := DDS_SAVE_FLAGS;
  821. Desc.Caps.Caps1 := DDSCAPS_TEXTURE;
  822. Desc.PixelFormat.Size := SizeOf(Desc.PixelFormat);
  823. Desc.PitchOrLinearSize := MainImage.Size;
  824. ImageCount := MipMapCount;
  825. if MipMapCount > 1 then
  826. begin
  827. // Set proper flags if we have some mipmaps to be saved
  828. Desc.Flags := Desc.Flags or DDSD_MIPMAPCOUNT;
  829. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_MIPMAP or DDSCAPS_COMPLEX;
  830. Desc.MipMaps := MipMapCount;
  831. end;
  832. if IsCubeMap then
  833. begin
  834. // Set proper cube map flags - number of stored faces is taken
  835. // from FSaveDepth
  836. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  837. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_CUBEMAP;
  838. J := DDSCAPS2_POSITIVEX;
  839. for I := 0 to FSaveDepth - 1 do
  840. begin
  841. Desc.Caps.Caps2 := Desc.Caps.Caps2 or J;
  842. J := J shl 1;
  843. end;
  844. ImageCount := FSaveDepth * FSaveMipMapCount;
  845. end
  846. else if IsVolume then
  847. begin
  848. // Set proper flags for volume texture
  849. Desc.Flags := Desc.Flags or DDSD_DEPTH;
  850. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  851. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_VOLUME;
  852. Desc.Depth := FSaveDepth;
  853. ImageCount := GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount);
  854. end;
  855. // Now we set DDS pixel format for main image
  856. if FmtInfo.IsSpecial or FmtInfo.IsFloatingPoint or
  857. (FmtInfo.BytesPerPixel > 4) then
  858. begin
  859. Desc.PixelFormat.Flags := DDPF_FOURCC;
  860. case Format of
  861. ifA16B16G16R16: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16;
  862. ifR32F: Desc.PixelFormat.FourCC := D3DFMT_R32F;
  863. ifA32B32G32R32F: Desc.PixelFormat.FourCC := D3DFMT_A32B32G32R32F;
  864. ifR16F: Desc.PixelFormat.FourCC := D3DFMT_R16F;
  865. ifA16B16G16R16F: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16F;
  866. ifDXT1: Desc.PixelFormat.FourCC := FOURCC_DXT1;
  867. ifDXT3: Desc.PixelFormat.FourCC := FOURCC_DXT3;
  868. ifDXT5: Desc.PixelFormat.FourCC := FOURCC_DXT5;
  869. ifATI1N: Desc.PixelFormat.FourCC := FOURCC_ATI1;
  870. ifATI2N: Desc.PixelFormat.FourCC := FOURCC_ATI2;
  871. end;
  872. end
  873. else if FmtInfo.HasGrayChannel then
  874. begin
  875. Desc.PixelFormat.Flags := DDPF_LUMINANCE;
  876. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  877. case Format of
  878. ifGray8: Desc.PixelFormat.RedMask := 255;
  879. ifGray16: Desc.PixelFormat.RedMask := 65535;
  880. ifA8Gray8:
  881. begin
  882. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS;
  883. Desc.PixelFormat.RedMask := 255;
  884. Desc.PixelFormat.AlphaMask := 65280;
  885. end;
  886. end;
  887. end
  888. else
  889. begin
  890. Desc.PixelFormat.Flags := DDPF_RGB;
  891. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  892. if FmtInfo.HasAlphaChannel then
  893. begin
  894. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS;
  895. Desc.PixelFormat.AlphaMask := $FF000000;
  896. end;
  897. if FmtInfo.BytesPerPixel > 2 then
  898. begin
  899. Desc.PixelFormat.RedMask := $00FF0000;
  900. Desc.PixelFormat.GreenMask := $0000FF00;
  901. Desc.PixelFormat.BlueMask := $000000FF;
  902. end
  903. else
  904. begin
  905. Desc.PixelFormat.AlphaMask := FmtInfo.PixelFormat.ABitMask;
  906. Desc.PixelFormat.RedMask := FmtInfo.PixelFormat.RBitMask;
  907. Desc.PixelFormat.GreenMask := FmtInfo.PixelFormat.GBitMask;
  908. Desc.PixelFormat.BlueMask := FmtInfo.PixelFormat.BBitMask;
  909. end;
  910. end;
  911. // Header and main image are written to output
  912. Write(Handle, @Hdr, SizeOf(Hdr));
  913. Write(Handle, MainImage.Bits, MainImage.Size);
  914. // Write the rest of the images and convert them to
  915. // the same format as main image if necessary and ensure proper mipmap
  916. // simensions too.
  917. for I := MainIdx + 1 to MainIdx + ImageCount - 1 do
  918. begin
  919. // Get proper dimensions for this level
  920. ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth,
  921. IsCubeMap, IsVolume, CurrentWidth, CurrentHeight);
  922. // Check if input image for this level has the right size and format
  923. NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight));
  924. NeedsConvert := not (Images[I].Format = Format);
  925. if NeedsResize or NeedsConvert then
  926. begin
  927. // Input image must be resized or converted to different format
  928. // to become valid mipmap level
  929. InitImage(ImageToSave);
  930. CloneImage(Images[I], ImageToSave);
  931. if NeedsConvert then
  932. ConvertImage(ImageToSave, Format);
  933. if NeedsResize then
  934. ResizeImage(ImageToSave, CurrentWidth, CurrentHeight, rfBilinear);
  935. end
  936. else
  937. // Input image can be used without any changes
  938. ImageToSave := Images[I];
  939. // Write level data and release temp image if necessary
  940. Write(Handle, ImageToSave.Bits, ImageToSave.Size);
  941. if Images[I].Bits <> ImageToSave.Bits then
  942. FreeImage(ImageToSave);
  943. end;
  944. Result := True;
  945. finally
  946. if MustBeFreed then
  947. FreeImage(MainImage);
  948. end;
  949. end;
  950. procedure TDDSFileFormat.ConvertToSupported(var Image: TImageData;
  951. const Info: TImageFormatInfo);
  952. var
  953. ConvFormat: TImageFormat;
  954. begin
  955. if Info.IsIndexed or Info.IsSpecial then
  956. // convert indexed and unsupported special formatd to A8R8G8B8
  957. ConvFormat := ifA8R8G8B8
  958. else if Info.IsFloatingPoint then
  959. begin
  960. if Info.Format = ifA16R16G16B16F then
  961. // only swap channels here
  962. ConvFormat := ifA16B16G16R16F
  963. else
  964. // convert other floating point formats to A32B32G32R32F
  965. ConvFormat := ifA32B32G32R32F
  966. end
  967. else if Info.HasGrayChannel then
  968. begin
  969. if Info.HasAlphaChannel then
  970. // convert grayscale with alpha to A8Gray8
  971. ConvFormat := ifA8Gray8
  972. else if Info.BytesPerPixel = 1 then
  973. // convert 8bit grayscale to Gray8
  974. ConvFormat := ifGray8
  975. else
  976. // convert 16-64bit grayscales to Gray16
  977. ConvFormat := ifGray16;
  978. end
  979. else if Info.BytesPerPixel > 4 then
  980. ConvFormat := ifA16B16G16R16
  981. else if Info.HasAlphaChannel then
  982. // convert the other images with alpha channel to A8R8G8B8
  983. ConvFormat := ifA8R8G8B8
  984. else
  985. // convert the other formats to X8R8G8B8
  986. ConvFormat := ifX8R8G8B8;
  987. ConvertImage(Image, ConvFormat);
  988. end;
  989. function TDDSFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  990. var
  991. Hdr: TDDSFileHeader;
  992. ReadCount: LongInt;
  993. begin
  994. Result := False;
  995. if Handle <> nil then
  996. with GetIO do
  997. begin
  998. ReadCount := Read(Handle, @Hdr, SizeOf(Hdr));
  999. Seek(Handle, -ReadCount, smFromCurrent);
  1000. Result := (Hdr.Magic = DDSMagic) and (ReadCount = SizeOf(Hdr)) and
  1001. ((Hdr.Desc.Caps.Caps1 and DDSCAPS_TEXTURE) = DDSCAPS_TEXTURE);
  1002. end;
  1003. end;
  1004. initialization
  1005. RegisterImageFileFormat(TDDSFileFormat);
  1006. {
  1007. File Notes:
  1008. -- TODOS ----------------------------------------------------
  1009. - nothing now
  1010. -- 0.77.1 ----------------------------------------------------
  1011. - Texture and D3D specific info stored in DDS is now available as metadata
  1012. (loading).
  1013. - Added support for loading DDS files with DX10 extension
  1014. (http://msdn.microsoft.com/en-us/library/windows/desktop/bb943991(v=vs.85).aspx)
  1015. and few compatibility fixes.
  1016. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  1017. - Added support for 3Dc ATI1/2 formats.
  1018. -- 0.23 Changes/Bug Fixes -----------------------------------
  1019. - Saved DDS with mipmaps now correctly defineds COMPLEX flag.
  1020. - Fixed loading of RGB DDS files that use pitch and have mipmaps -
  1021. mipmaps were loaded wrongly.
  1022. -- 0.21 Changes/Bug Fixes -----------------------------------
  1023. - Changed saving behaviour a bit: mipmaps are inlcuded automatically for
  1024. 2D textures if input image array has more than 1 image (no need to
  1025. set SaveMipMapCount manually).
  1026. - Mipmap levels are now saved with proper dimensions when saving DDS files.
  1027. - Made some changes to not be so strict when loading DDS files.
  1028. Many programs seem to save them in non-standard format
  1029. (by MS DDS File Reference).
  1030. - Added missing ifX8R8G8B8 to SupportedFormats, MakeCompatible failed
  1031. when image was converted to this format (inside).
  1032. - MakeCompatible method moved to base class, put ConvertToSupported here.
  1033. GetSupportedFormats removed, it is now set in constructor.
  1034. - Fixed bug that sometimes saved non-standard DDS files and another
  1035. one that caused crash when these files were loaded.
  1036. - Changed extensions to filename masks.
  1037. - Changed SaveData, LoadData, and MakeCompatible methods according
  1038. to changes in base class in Imaging unit.
  1039. -- 0.19 Changes/Bug Fixes -----------------------------------
  1040. - added support for half-float image formats
  1041. - change in LoadData to allow support for more images
  1042. in one stream loading
  1043. -- 0.17 Changes/Bug Fixes -----------------------------------
  1044. - fixed bug in TestFormat which does not recognize many DDS files
  1045. - changed pitch/linearsize handling in DDS loading code to
  1046. load DDS files produced by NVidia's Photoshop plugin
  1047. }
  1048. end.