Repo for the search and displace ingest module that takes odf, docx and pdf and transforms it into .md to be used with search and displace operations
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1145 lines
43 KiB

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