ImagingDds.pas 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864
  1. {
  2. $Id$
  3. Vampyre Imaging Library
  4. by Marek Mauder
  5. http://imaginglib.sourceforge.net
  6. The contents of this file are used with permission, subject to the Mozilla
  7. Public License Version 1.1 (the "License"); you may not use this file except
  8. in compliance with the License. You may obtain a copy of the License at
  9. http://www.mozilla.org/MPL/MPL-1.1.html
  10. Software distributed under the License is distributed on an "AS IS" basis,
  11. WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
  12. the specific language governing rights and limitations under the License.
  13. Alternatively, the contents of this file may be used under the terms of the
  14. GNU Lesser General Public License (the "LGPL License"), in which case the
  15. provisions of the LGPL License are applicable instead of those above.
  16. If you wish to allow use of your version of this file only under the terms
  17. of the LGPL License and not to allow others to use your version of this file
  18. under the MPL, indicate your decision by deleting the provisions above and
  19. replace them with the notice and other provisions required by the LGPL
  20. License. If you do not delete the provisions above, a recipient may use
  21. your version of this file under either the MPL or the LGPL License.
  22. For more information about the LGPL: http://www.gnu.org/copyleft/lesser.html
  23. }
  24. { This unit contains image format loader/saver for DirectDraw Surface images.}
  25. unit ImagingDds;
  26. {$I ImagingOptions.inc}
  27. interface
  28. uses
  29. ImagingTypes, Imaging, ImagingUtility, ImagingFormats;
  30. type
  31. { Class for loading and saving Microsoft DirectDraw surfaces.
  32. It can load/save all D3D formats which have coresponding
  33. TImageFormat. It supports plain textures, cube textures and
  34. volume textures, all of these can have mipmaps. It can also
  35. load some formats which have no exact TImageFormat, but can be easily
  36. converted to one (bump map formats).
  37. You can get some information about last loaded DDS file by calling
  38. GetOption with ImagingDDSLoadedXXX options and you can set some
  39. saving options by calling SetOption with ImagingDDSSaveXXX or you can
  40. simply use properties of this class.
  41. Note that when saving cube maps and volumes input image array must contain
  42. at least number of images to build cube/volume based on current
  43. Depth and MipMapCount settings.}
  44. TDDSFileFormat = class(TImageFileFormat)
  45. protected
  46. FLoadedCubeMap: LongBool;
  47. FLoadedVolume: LongBool;
  48. FLoadedMipMapCount: LongInt;
  49. FLoadedDepth: LongInt;
  50. FSaveCubeMap: LongBool;
  51. FSaveVolume: LongBool;
  52. FSaveMipMapCount: LongInt;
  53. FSaveDepth: LongInt;
  54. procedure ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt;
  55. IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt);
  56. function LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  57. OnlyFirstLevel: Boolean): Boolean; override;
  58. function SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  59. Index: LongInt): Boolean; override;
  60. procedure ConvertToSupported(var Image: TImageData;
  61. const Info: TImageFormatInfo); override;
  62. public
  63. constructor Create; override;
  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. implementation
  88. const
  89. SDDSFormatName = 'DirectDraw Surface';
  90. SDDSMasks = '*.dds';
  91. DDSSupportedFormats: TImageFormats = [ifR8G8B8, ifA8R8G8B8, ifX8R8G8B8,
  92. ifA1R5G5B5, ifA4R4G4B4, ifX1R5G5B5, ifX4R4G4B4, ifR5G6B5, ifA16B16G16R16,
  93. ifR32F, ifA32B32G32R32F, ifR16F, ifA16B16G16R16F, ifR3G3B2, ifGray8, ifA8Gray8,
  94. ifGray16, ifDXT1, ifDXT3, ifDXT5, ifATI1N, ifATI2N];
  95. const
  96. { Four character codes.}
  97. DDSMagic = LongWord(Byte('D') or (Byte('D') shl 8) or (Byte('S') shl 16) or
  98. (Byte(' ') shl 24));
  99. FOURCC_DXT1 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  100. (Byte('1') shl 24));
  101. FOURCC_DXT3 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  102. (Byte('3') shl 24));
  103. FOURCC_DXT5 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  104. (Byte('5') shl 24));
  105. FOURCC_ATI1 = LongWord(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  106. (Byte('1') shl 24));
  107. FOURCC_ATI2 = LongWord(Byte('A') or (Byte('T') shl 8) or (Byte('I') shl 16) or
  108. (Byte('2') shl 24));
  109. { Some D3DFORMAT values used in DDS files as FourCC value.}
  110. D3DFMT_A16B16G16R16 = 36;
  111. D3DFMT_R32F = 114;
  112. D3DFMT_A32B32G32R32F = 116;
  113. D3DFMT_R16F = 111;
  114. D3DFMT_A16B16G16R16F = 113;
  115. { Constans used by TDDSurfaceDesc2.Flags.}
  116. DDSD_CAPS = $00000001;
  117. DDSD_HEIGHT = $00000002;
  118. DDSD_WIDTH = $00000004;
  119. DDSD_PITCH = $00000008;
  120. DDSD_PIXELFORMAT = $00001000;
  121. DDSD_MIPMAPCOUNT = $00020000;
  122. DDSD_LINEARSIZE = $00080000;
  123. DDSD_DEPTH = $00800000;
  124. { Constans used by TDDSPixelFormat.Flags.}
  125. DDPF_ALPHAPIXELS = $00000001; // used by formats which contain alpha
  126. DDPF_FOURCC = $00000004; // used by DXT and large ARGB formats
  127. DDPF_RGB = $00000040; // used by RGB formats
  128. DDPF_LUMINANCE = $00020000; // used by formats like D3DFMT_L16
  129. DDPF_BUMPLUMINANCE = $00040000; // used by mixed signed-unsigned formats
  130. DDPF_BUMPDUDV = $00080000; // used by signed formats
  131. { Constans used by TDDSCaps.Caps1.}
  132. DDSCAPS_COMPLEX = $00000008;
  133. DDSCAPS_TEXTURE = $00001000;
  134. DDSCAPS_MIPMAP = $00400000;
  135. { Constans used by TDDSCaps.Caps2.}
  136. DDSCAPS2_CUBEMAP = $00000200;
  137. DDSCAPS2_POSITIVEX = $00000400;
  138. DDSCAPS2_NEGATIVEX = $00000800;
  139. DDSCAPS2_POSITIVEY = $00001000;
  140. DDSCAPS2_NEGATIVEY = $00002000;
  141. DDSCAPS2_POSITIVEZ = $00004000;
  142. DDSCAPS2_NEGATIVEZ = $00008000;
  143. DDSCAPS2_VOLUME = $00200000;
  144. { Flags for TDDSurfaceDesc2.Flags used when saving DDS file.}
  145. DDS_SAVE_FLAGS = DDSD_CAPS or DDSD_PIXELFORMAT or DDSD_WIDTH or
  146. DDSD_HEIGHT or DDSD_LINEARSIZE;
  147. type
  148. { Stores the pixel format information.}
  149. TDDPixelFormat = packed record
  150. Size: LongWord; // Size of the structure = 32 bytes
  151. Flags: LongWord; // Flags to indicate valid fields
  152. FourCC: LongWord; // Four-char code for compressed textures (DXT)
  153. BitCount: LongWord; // Bits per pixel if uncomp. usually 16,24 or 32
  154. RedMask: LongWord; // Bit mask for the Red component
  155. GreenMask: LongWord; // Bit mask for the Green component
  156. BlueMask: LongWord; // Bit mask for the Blue component
  157. AlphaMask: LongWord; // Bit mask for the Alpha component
  158. end;
  159. { Specifies capabilities of surface.}
  160. TDDSCaps = packed record
  161. Caps1: LongWord; // Should always include DDSCAPS_TEXTURE
  162. Caps2: LongWord; // For cubic environment maps
  163. Reserved: array[0..1] of LongWord; // Reserved
  164. end;
  165. { Record describing DDS file contents.}
  166. TDDSurfaceDesc2 = packed record
  167. Size: LongWord; // Size of the structure = 124 Bytes
  168. Flags: LongWord; // Flags to indicate valid fields
  169. Height: LongWord; // Height of the main image in pixels
  170. Width: LongWord; // Width of the main image in pixels
  171. PitchOrLinearSize: LongWord; // For uncomp formats number of bytes per
  172. // scanline. For comp it is the size in
  173. // bytes of the main image
  174. Depth: LongWord; // Only for volume text depth of the volume
  175. MipMaps: LongInt; // Total number of levels in the mipmap chain
  176. Reserved1: array[0..10] of LongWord; // Reserved
  177. PixelFormat: TDDPixelFormat; // Format of the pixel data
  178. Caps: TDDSCaps; // Capabilities
  179. Reserved2: LongWord; // Reserved
  180. end;
  181. { DDS file header.}
  182. TDDSFileHeader = packed record
  183. Magic: LongWord; // File format magic
  184. Desc: TDDSurfaceDesc2; // Surface description
  185. end;
  186. { TDDSFileFormat class implementation }
  187. constructor TDDSFileFormat.Create;
  188. begin
  189. inherited Create;
  190. FName := SDDSFormatName;
  191. FCanLoad := True;
  192. FCanSave := True;
  193. FIsMultiImageFormat := True;
  194. FSupportedFormats := DDSSupportedFormats;
  195. FSaveCubeMap := False;
  196. FSaveVolume := False;
  197. FSaveMipMapCount := 1;
  198. FSaveDepth := 1;
  199. AddMasks(SDDSMasks);
  200. RegisterOption(ImagingDDSLoadedCubeMap, @FLoadedCubeMap);
  201. RegisterOption(ImagingDDSLoadedVolume, @FLoadedVolume);
  202. RegisterOption(ImagingDDSLoadedMipMapCount, @FLoadedMipMapCount);
  203. RegisterOption(ImagingDDSLoadedDepth, @FLoadedDepth);
  204. RegisterOption(ImagingDDSSaveCubeMap, @FSaveCubeMap);
  205. RegisterOption(ImagingDDSSaveVolume, @FSaveVolume);
  206. RegisterOption(ImagingDDSSaveMipMapCount, @FSaveMipMapCount);
  207. RegisterOption(ImagingDDSSaveDepth, @FSaveDepth);
  208. end;
  209. procedure TDDSFileFormat.CheckOptionsValidity;
  210. begin
  211. if FSaveCubeMap then
  212. FSaveVolume := False;
  213. if FSaveVolume then
  214. FSaveCubeMap := False;
  215. if FSaveDepth < 1 then
  216. FSaveDepth := 1;
  217. if FSaveMipMapCount < 1 then
  218. FSaveMipMapCount := 1;
  219. end;
  220. procedure TDDSFileFormat.ComputeSubDimensions(Idx, Width, Height, MipMaps, Depth: LongInt;
  221. IsCubeMap, IsVolume: Boolean; var CurWidth, CurHeight: LongInt);
  222. var
  223. I, Last, Shift: LongInt;
  224. begin
  225. CurWidth := Width;
  226. CurHeight := Height;
  227. if MipMaps > 1 then
  228. begin
  229. if not IsVolume then
  230. begin
  231. if IsCubeMap then
  232. begin
  233. // Cube maps are stored like this
  234. // Face 0 mimap 0
  235. // Face 0 mipmap 1
  236. // ...
  237. // Face 1 mipmap 0
  238. // Face 1 mipmap 1
  239. // ...
  240. // Modify index so later in for loop we iterate less times
  241. Idx := Idx - ((Idx div MipMaps) * MipMaps);
  242. end;
  243. for I := 0 to Idx - 1 do
  244. begin
  245. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  246. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  247. end;
  248. end
  249. else
  250. begin
  251. // Volume textures are stored in DDS files like this:
  252. // Slice 0 mipmap 0
  253. // Slice 1 mipmap 0
  254. // Slice 2 mipmap 0
  255. // Slice 3 mipmap 0
  256. // Slice 0 mipmap 1
  257. // Slice 1 mipmap 1
  258. // Slice 0 mipmap 2
  259. // Slice 0 mipmap 3 ...
  260. Shift := 0;
  261. Last := Depth;
  262. while Idx > Last - 1 do
  263. begin
  264. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  265. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  266. if (CurWidth = 1) and (CurHeight = 1) then
  267. Break;
  268. Inc(Shift);
  269. Inc(Last, ClampInt(Depth shr Shift, 1, Depth));
  270. end;
  271. end;
  272. end;
  273. end;
  274. function TDDSFileFormat.LoadData(Handle: TImagingHandle;
  275. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean): Boolean;
  276. var
  277. Hdr: TDDSFileHeader;
  278. SrcFormat: TImageFormat;
  279. FmtInfo: TImageFormatInfo;
  280. NeedsSwapChannels: Boolean;
  281. CurrentWidth, CurrentHeight, ImageCount, LoadSize, I, PitchOrLinear: LongInt;
  282. Data: PByte;
  283. UseAsPitch: Boolean;
  284. UseAsLinear: Boolean;
  285. function MasksEqual(const DDPF: TDDPixelFormat; PF: PPixelFormatInfo): Boolean;
  286. begin
  287. Result := (DDPF.AlphaMask = PF.ABitMask) and
  288. (DDPF.RedMask = PF.RBitMask) and (DDPF.GreenMask = PF.GBitMask) and
  289. (DDPF.BlueMask = PF.BBitMask);
  290. end;
  291. begin
  292. Result := False;
  293. ImageCount := 1;
  294. FLoadedMipMapCount := 1;
  295. FLoadedDepth := 1;
  296. FLoadedVolume := False;
  297. FLoadedCubeMap := False;
  298. with GetIO, Hdr, Hdr.Desc.PixelFormat do
  299. begin
  300. Read(Handle, @Hdr, SizeOF(Hdr));
  301. {
  302. // Set position to the end of the header (for possible future versions
  303. // ith larger header)
  304. Seek(Handle, Hdr.Desc.Size + SizeOf(Hdr.Magic) - SizeOf(Hdr),
  305. smFromCurrent);
  306. }
  307. SrcFormat := ifUnknown;
  308. NeedsSwapChannels := False;
  309. // Get image data format
  310. if (Flags and DDPF_FOURCC) = DDPF_FOURCC then
  311. begin
  312. // Handle FourCC and large ARGB formats
  313. case FourCC of
  314. D3DFMT_A16B16G16R16: SrcFormat := ifA16B16G16R16;
  315. D3DFMT_R32F: SrcFormat := ifR32F;
  316. D3DFMT_A32B32G32R32F: SrcFormat := ifA32B32G32R32F;
  317. D3DFMT_R16F: SrcFormat := ifR16F;
  318. D3DFMT_A16B16G16R16F: SrcFormat := ifA16B16G16R16F;
  319. FOURCC_DXT1: SrcFormat := ifDXT1;
  320. FOURCC_DXT3: SrcFormat := ifDXT3;
  321. FOURCC_DXT5: SrcFormat := ifDXT5;
  322. FOURCC_ATI1: SrcFormat := ifATI1N;
  323. FOURCC_ATI2: SrcFormat := ifATI2N;
  324. end;
  325. end
  326. else if (Flags and DDPF_RGB) = DDPF_RGB then
  327. begin
  328. // Handle RGB formats
  329. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  330. begin
  331. // Handle RGB with alpha formats
  332. case BitCount of
  333. 16:
  334. begin
  335. if MasksEqual(Desc.PixelFormat,
  336. GetFormatInfo(ifA4R4G4B4).PixelFormat) then
  337. SrcFormat := ifA4R4G4B4;
  338. if MasksEqual(Desc.PixelFormat,
  339. GetFormatInfo(ifA1R5G5B5).PixelFormat) then
  340. SrcFormat := ifA1R5G5B5;
  341. end;
  342. 32:
  343. begin
  344. SrcFormat := ifA8R8G8B8;
  345. if BlueMask = $00FF0000 then
  346. NeedsSwapChannels := True;
  347. end;
  348. end;
  349. end
  350. else
  351. begin
  352. // Handle RGB without alpha formats
  353. case BitCount of
  354. 8:
  355. if MasksEqual(Desc.PixelFormat,
  356. GetFormatInfo(ifR3G3B2).PixelFormat) then
  357. SrcFormat := ifR3G3B2;
  358. 16:
  359. begin
  360. if MasksEqual(Desc.PixelFormat,
  361. GetFormatInfo(ifX4R4G4B4).PixelFormat) then
  362. SrcFormat := ifX4R4G4B4;
  363. if MasksEqual(Desc.PixelFormat,
  364. GetFormatInfo(ifX1R5G5B5).PixelFormat) then
  365. SrcFormat := ifX1R5G5B5;
  366. if MasksEqual(Desc.PixelFormat,
  367. GetFormatInfo(ifR5G6B5).PixelFormat) then
  368. SrcFormat := ifR5G6B5;
  369. end;
  370. 24: SrcFormat := ifR8G8B8;
  371. 32:
  372. begin
  373. SrcFormat := ifX8R8G8B8;
  374. if BlueMask = $00FF0000 then
  375. NeedsSwapChannels := True;
  376. end;
  377. end;
  378. end;
  379. end
  380. else if (Flags and DDPF_LUMINANCE) = DDPF_LUMINANCE then
  381. begin
  382. // Handle luminance formats
  383. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  384. begin
  385. // Handle luminance with alpha formats
  386. if BitCount = 16 then
  387. SrcFormat := ifA8Gray8;
  388. end
  389. else
  390. begin
  391. // Handle luminance without alpha formats
  392. case BitCount of
  393. 8: SrcFormat := ifGray8;
  394. 16: SrcFormat := ifGray16;
  395. end;
  396. end;
  397. end
  398. else if (Flags and DDPF_BUMPLUMINANCE) = DDPF_BUMPLUMINANCE then
  399. begin
  400. // Handle mixed bump-luminance formats like D3DFMT_X8L8V8U8
  401. case BitCount of
  402. 32:
  403. if BlueMask = $00FF0000 then
  404. begin
  405. SrcFormat := ifX8R8G8B8; // D3DFMT_X8L8V8U8
  406. NeedsSwapChannels := True;
  407. end;
  408. end;
  409. end
  410. else if (Flags and DDPF_BUMPDUDV) = DDPF_BUMPDUDV then
  411. begin
  412. // Handle bumpmap formats like D3DFMT_Q8W8V8U8
  413. case BitCount of
  414. 16: SrcFormat := ifA8Gray8; // D3DFMT_V8U8
  415. 32:
  416. if AlphaMask = $FF000000 then
  417. begin
  418. SrcFormat := ifA8R8G8B8; // D3DFMT_Q8W8V8U8
  419. NeedsSwapChannels := True;
  420. end;
  421. 64: SrcFormat := ifA16B16G16R16; // D3DFMT_Q16W16V16U16
  422. end;
  423. end;
  424. // If DDS format is not supported we will exit
  425. if SrcFormat = ifUnknown then Exit;
  426. // File contains mipmaps for each subimage.
  427. { Some DDS writers ignore setting proper Caps and Flags so
  428. this check is not usable:
  429. if ((Desc.Caps.Caps1 and DDSCAPS_MIPMAP) = DDSCAPS_MIPMAP) and
  430. ((Desc.Flags and DDSD_MIPMAPCOUNT) = DDSD_MIPMAPCOUNT) then}
  431. if Desc.MipMaps > 1 then
  432. begin
  433. FLoadedMipMapCount := Desc.MipMaps;
  434. ImageCount := Desc.MipMaps;
  435. end;
  436. // File stores volume texture
  437. if ((Desc.Caps.Caps2 and DDSCAPS2_VOLUME) = DDSCAPS2_VOLUME) and
  438. ((Desc.Flags and DDSD_DEPTH) = DDSD_DEPTH) then
  439. begin
  440. FLoadedVolume := True;
  441. FLoadedDepth := Desc.Depth;
  442. ImageCount := GetVolumeLevelCount(Desc.Depth, ImageCount);
  443. end;
  444. // File stores cube texture
  445. if (Desc.Caps.Caps2 and DDSCAPS2_CUBEMAP) = DDSCAPS2_CUBEMAP then
  446. begin
  447. FLoadedCubeMap := True;
  448. I := 0;
  449. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEX) = DDSCAPS2_POSITIVEX then Inc(I);
  450. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEY) = DDSCAPS2_POSITIVEY then Inc(I);
  451. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEZ) = DDSCAPS2_POSITIVEZ then Inc(I);
  452. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEX) = DDSCAPS2_NEGATIVEX then Inc(I);
  453. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEY) = DDSCAPS2_NEGATIVEY then Inc(I);
  454. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEZ) = DDSCAPS2_NEGATIVEZ then Inc(I);
  455. FLoadedDepth := I;
  456. ImageCount := ImageCount * I;
  457. end;
  458. // Allocate and load all images in file
  459. FmtInfo := GetFormatInfo(SrcFormat);
  460. SetLength(Images, ImageCount);
  461. // Compute the pitch or get if from file if present
  462. UseAsPitch := (Desc.Flags and DDSD_PITCH) = DDSD_PITCH;
  463. UseAsLinear := (Desc.Flags and DDSD_LINEARSIZE) = DDSD_LINEARSIZE;
  464. // Use linear as default if none is set
  465. if not UseAsPitch and not UseAsLinear then
  466. UseAsLinear := True;
  467. // Main image pitch or linear size
  468. PitchOrLinear := Desc.PitchOrLinearSize;
  469. for I := 0 to ImageCount - 1 do
  470. begin
  471. // Compute dimensions of surrent subimage based on texture type and
  472. // number of mipmaps
  473. ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth,
  474. FloadedCubeMap, FLoadedVolume, CurrentWidth, CurrentHeight);
  475. NewImage(CurrentWidth, CurrentHeight, SrcFormat, Images[I]);
  476. if (I > 0) or (PitchOrLinear = 0) then
  477. begin
  478. // Compute pitch or linear size for mipmap levels, or even for main image
  479. // since some formats do not fill pitch nor size
  480. if UseAsLinear then
  481. PitchOrLinear := FmtInfo.GetPixelsSize(SrcFormat, CurrentWidth, CurrentHeight)
  482. else
  483. PitchOrLinear := (CurrentWidth * FmtInfo.BytesPerPixel + 3) div 4 * 4; // must be DWORD aligned
  484. end;
  485. if UseAsLinear then
  486. LoadSize := PitchOrLinear
  487. else
  488. LoadSize := CurrentHeight * PitchOrLinear;
  489. if UseAsLinear or (LoadSize = Images[I].Size) then
  490. begin
  491. // If DDS does not use Pitch we can simply copy data
  492. Read(Handle, Images[I].Bits, LoadSize)
  493. end
  494. else
  495. begin
  496. // If DDS uses Pitch we must load aligned scanlines
  497. // and then remove padding
  498. GetMem(Data, LoadSize);
  499. try
  500. Read(Handle, Data, LoadSize);
  501. RemovePadBytes(Data, Images[I].Bits, CurrentWidth, CurrentHeight,
  502. FmtInfo.BytesPerPixel, PitchOrLinear);
  503. finally
  504. FreeMem(Data);
  505. end;
  506. end;
  507. if NeedsSwapChannels then
  508. SwapChannels(Images[I], ChannelRed, ChannelBlue);
  509. end;
  510. Result := True;
  511. end;
  512. end;
  513. function TDDSFileFormat.SaveData(Handle: TImagingHandle;
  514. const Images: TDynImageDataArray; Index: LongInt): Boolean;
  515. var
  516. Hdr: TDDSFileHeader;
  517. MainImage, ImageToSave: TImageData;
  518. I, MainIdx, Len, ImageCount: LongInt;
  519. J: LongWord;
  520. FmtInfo: TImageFormatInfo;
  521. MustBeFreed: Boolean;
  522. Is2DTexture, IsCubeMap, IsVolume: Boolean;
  523. MipMapCount, CurrentWidth, CurrentHeight: LongInt;
  524. NeedsResize: Boolean;
  525. NeedsConvert: Boolean;
  526. begin
  527. Result := False;
  528. FillChar(Hdr, Sizeof(Hdr), 0);
  529. MainIdx := FFirstIdx;
  530. Len := FLastIdx - MainIdx + 1;
  531. // Some DDS saving rules:
  532. // 2D textures: Len is used as mipmap count (FSaveMipMapCount not used!).
  533. // Cube maps: FSaveDepth * FSaveMipMapCount images are used, if Len is
  534. // smaller than this file is saved as regular 2D texture.
  535. // Volume maps: GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) images are
  536. // used, if Len is smaller than this file is
  537. // saved as regular 2D texture.
  538. IsCubeMap := FSaveCubeMap;
  539. IsVolume := FSaveVolume;
  540. MipMapCount := FSaveMipMapCount;
  541. if IsCubeMap then
  542. begin
  543. // Check if we have enough images on Input to save cube map
  544. if Len < FSaveDepth * FSaveMipMapCount then
  545. IsCubeMap := False;
  546. end
  547. else if IsVolume then
  548. begin
  549. // Check if we have enough images on Input to save volume texture
  550. if Len < GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) then
  551. IsVolume := False;
  552. end;
  553. Is2DTexture := not IsCubeMap and not IsVolume;
  554. if Is2DTexture then
  555. begin
  556. // Get number of mipmaps used with 2D texture
  557. MipMapCount := Min(Len, GetNumMipMapLevels(Images[MainIdx].Width, Images[MainIdx].Height));
  558. end;
  559. // we create compatible main image and fill headers
  560. if MakeCompatible(Images[MainIdx], MainImage, MustBeFreed) then
  561. with GetIO, MainImage, Hdr do
  562. try
  563. FmtInfo := GetFormatInfo(Format);
  564. Magic := DDSMagic;
  565. Desc.Size := SizeOf(Desc);
  566. Desc.Width := Width;
  567. Desc.Height := Height;
  568. Desc.Flags := DDS_SAVE_FLAGS;
  569. Desc.Caps.Caps1 := DDSCAPS_TEXTURE;
  570. Desc.PixelFormat.Size := SizeOf(Desc.PixelFormat);
  571. Desc.PitchOrLinearSize := MainImage.Size;
  572. ImageCount := MipMapCount;
  573. if MipMapCount > 1 then
  574. begin
  575. // Set proper flags if we have some mipmaps to be saved
  576. Desc.Flags := Desc.Flags or DDSD_MIPMAPCOUNT;
  577. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_MIPMAP or DDSCAPS_COMPLEX;
  578. Desc.MipMaps := MipMapCount;
  579. end;
  580. if IsCubeMap then
  581. begin
  582. // Set proper cube map flags - number of stored faces is taken
  583. // from FSaveDepth
  584. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  585. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_CUBEMAP;
  586. J := DDSCAPS2_POSITIVEX;
  587. for I := 0 to FSaveDepth - 1 do
  588. begin
  589. Desc.Caps.Caps2 := Desc.Caps.Caps2 or J;
  590. J := J shl 1;
  591. end;
  592. ImageCount := FSaveDepth * FSaveMipMapCount;
  593. end
  594. else if IsVolume then
  595. begin
  596. // Set proper flags for volume texture
  597. Desc.Flags := Desc.Flags or DDSD_DEPTH;
  598. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  599. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_VOLUME;
  600. Desc.Depth := FSaveDepth;
  601. ImageCount := GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount);
  602. end;
  603. // Now we set DDS pixel format for main image
  604. if FmtInfo.IsSpecial or FmtInfo.IsFloatingPoint or
  605. (FmtInfo.BytesPerPixel > 4) then
  606. begin
  607. Desc.PixelFormat.Flags := DDPF_FOURCC;
  608. case Format of
  609. ifA16B16G16R16: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16;
  610. ifR32F: Desc.PixelFormat.FourCC := D3DFMT_R32F;
  611. ifA32B32G32R32F: Desc.PixelFormat.FourCC := D3DFMT_A32B32G32R32F;
  612. ifR16F: Desc.PixelFormat.FourCC := D3DFMT_R16F;
  613. ifA16B16G16R16F: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16F;
  614. ifDXT1: Desc.PixelFormat.FourCC := FOURCC_DXT1;
  615. ifDXT3: Desc.PixelFormat.FourCC := FOURCC_DXT3;
  616. ifDXT5: Desc.PixelFormat.FourCC := FOURCC_DXT5;
  617. ifATI1N: Desc.PixelFormat.FourCC := FOURCC_ATI1;
  618. ifATI2N: Desc.PixelFormat.FourCC := FOURCC_ATI2;
  619. end;
  620. end
  621. else if FmtInfo.HasGrayChannel then
  622. begin
  623. Desc.PixelFormat.Flags := DDPF_LUMINANCE;
  624. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  625. case Format of
  626. ifGray8: Desc.PixelFormat.RedMask := 255;
  627. ifGray16: Desc.PixelFormat.RedMask := 65535;
  628. ifA8Gray8:
  629. begin
  630. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS;
  631. Desc.PixelFormat.RedMask := 255;
  632. Desc.PixelFormat.AlphaMask := 65280;
  633. end;
  634. end;
  635. end
  636. else
  637. begin
  638. Desc.PixelFormat.Flags := DDPF_RGB;
  639. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  640. if FmtInfo.HasAlphaChannel then
  641. begin
  642. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or DDPF_ALPHAPIXELS;
  643. Desc.PixelFormat.AlphaMask := $FF000000;
  644. end;
  645. if FmtInfo.BytesPerPixel > 2 then
  646. begin
  647. Desc.PixelFormat.RedMask := $00FF0000;
  648. Desc.PixelFormat.GreenMask := $0000FF00;
  649. Desc.PixelFormat.BlueMask := $000000FF;
  650. end
  651. else
  652. begin
  653. Desc.PixelFormat.AlphaMask := FmtInfo.PixelFormat.ABitMask;
  654. Desc.PixelFormat.RedMask := FmtInfo.PixelFormat.RBitMask;
  655. Desc.PixelFormat.GreenMask := FmtInfo.PixelFormat.GBitMask;
  656. Desc.PixelFormat.BlueMask := FmtInfo.PixelFormat.BBitMask;
  657. end;
  658. end;
  659. // Header and main image are written to output
  660. Write(Handle, @Hdr, SizeOf(Hdr));
  661. Write(Handle, MainImage.Bits, MainImage.Size);
  662. // Write the rest of the images and convert them to
  663. // the same format as main image if necessary and ensure proper mipmap
  664. // simensions too.
  665. for I := MainIdx + 1 to MainIdx + ImageCount - 1 do
  666. begin
  667. // Get proper dimensions for this level
  668. ComputeSubDimensions(I, Desc.Width, Desc.Height, Desc.MipMaps, Desc.Depth,
  669. IsCubeMap, IsVolume, CurrentWidth, CurrentHeight);
  670. // Check if input image for this level has the right size and format
  671. NeedsResize := not ((Images[I].Width = CurrentWidth) and (Images[I].Height = CurrentHeight));
  672. NeedsConvert := not (Images[I].Format = Format);
  673. if NeedsResize or NeedsConvert then
  674. begin
  675. // Input image must be resized or converted to different format
  676. // to become valid mipmap level
  677. InitImage(ImageToSave);
  678. CloneImage(Images[I], ImageToSave);
  679. if NeedsConvert then
  680. ConvertImage(ImageToSave, Format);
  681. if NeedsResize then
  682. ResizeImage(ImageToSave, CurrentWidth, CurrentHeight, rfBilinear);
  683. end
  684. else
  685. // Input image can be used without any changes
  686. ImageToSave := Images[I];
  687. // Write level data and release temp image if necessary
  688. Write(Handle, ImageToSave.Bits, ImageToSave.Size);
  689. if Images[I].Bits <> ImageToSave.Bits then
  690. FreeImage(ImageToSave);
  691. end;
  692. Result := True;
  693. finally
  694. if MustBeFreed then
  695. FreeImage(MainImage);
  696. end;
  697. end;
  698. procedure TDDSFileFormat.ConvertToSupported(var Image: TImageData;
  699. const Info: TImageFormatInfo);
  700. var
  701. ConvFormat: TImageFormat;
  702. begin
  703. if Info.IsIndexed or Info.IsSpecial then
  704. // convert indexed and unsupported special formatd to A8R8G8B8
  705. ConvFormat := ifA8R8G8B8
  706. else if Info.IsFloatingPoint then
  707. begin
  708. if Info.Format = ifA16R16G16B16F then
  709. // only swap channels here
  710. ConvFormat := ifA16B16G16R16F
  711. else
  712. // convert other floating point formats to A32B32G32R32F
  713. ConvFormat := ifA32B32G32R32F
  714. end
  715. else if Info.HasGrayChannel then
  716. begin
  717. if Info.HasAlphaChannel then
  718. // convert grayscale with alpha to A8Gray8
  719. ConvFormat := ifA8Gray8
  720. else if Info.BytesPerPixel = 1 then
  721. // convert 8bit grayscale to Gray8
  722. ConvFormat := ifGray8
  723. else
  724. // convert 16-64bit grayscales to Gray16
  725. ConvFormat := ifGray16;
  726. end
  727. else if Info.BytesPerPixel > 4 then
  728. ConvFormat := ifA16B16G16R16
  729. else if Info.HasAlphaChannel then
  730. // convert the other images with alpha channel to A8R8G8B8
  731. ConvFormat := ifA8R8G8B8
  732. else
  733. // convert the other formats to X8R8G8B8
  734. ConvFormat := ifX8R8G8B8;
  735. ConvertImage(Image, ConvFormat);
  736. end;
  737. function TDDSFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  738. var
  739. Hdr: TDDSFileHeader;
  740. ReadCount: LongInt;
  741. begin
  742. Result := False;
  743. if Handle <> nil then
  744. with GetIO do
  745. begin
  746. ReadCount := Read(Handle, @Hdr, SizeOf(Hdr));
  747. Seek(Handle, -ReadCount, smFromCurrent);
  748. Result := (Hdr.Magic = DDSMagic) and (ReadCount = SizeOf(Hdr)) and
  749. ((Hdr.Desc.Caps.Caps1 and DDSCAPS_TEXTURE) = DDSCAPS_TEXTURE);
  750. end;
  751. end;
  752. initialization
  753. RegisterImageFileFormat(TDDSFileFormat);
  754. {
  755. File Notes:
  756. -- TODOS ----------------------------------------------------
  757. - nothing now
  758. -- 0.25.0 Changes/Bug Fixes ---------------------------------
  759. - Added support for 3Dc ATI1/2 formats.
  760. -- 0.23 Changes/Bug Fixes -----------------------------------
  761. - Saved DDS with mipmaps now correctly defineds COMPLEX flag.
  762. - Fixed loading of RGB DDS files that use pitch and have mipmaps -
  763. mipmaps were loaded wrongly.
  764. -- 0.21 Changes/Bug Fixes -----------------------------------
  765. - Changed saving behaviour a bit: mipmaps are inlcuded automatically for
  766. 2D textures if input image array has more than 1 image (no need to
  767. set SaveMipMapCount manually).
  768. - Mipmap levels are now saved with proper dimensions when saving DDS files.
  769. - Made some changes to not be so strict when loading DDS files.
  770. Many programs seem to save them in non-standard format
  771. (by MS DDS File Reference).
  772. - Added missing ifX8R8G8B8 to SupportedFormats, MakeCompatible failed
  773. when image was converted to this format (inside).
  774. - MakeCompatible method moved to base class, put ConvertToSupported here.
  775. GetSupportedFormats removed, it is now set in constructor.
  776. - Fixed bug that sometimes saved non-standard DDS files and another
  777. one that caused crash when these files were loaded.
  778. - Changed extensions to filename masks.
  779. - Changed SaveData, LoadData, and MakeCompatible methods according
  780. to changes in base class in Imaging unit.
  781. -- 0.19 Changes/Bug Fixes -----------------------------------
  782. - added support for half-float image formats
  783. - change in LoadData to allow support for more images
  784. in one stream loading
  785. -- 0.17 Changes/Bug Fixes -----------------------------------
  786. - fixed bug in TestFormat which does not recognize many DDS files
  787. - changed pitch/linearsize handling in DDS loading code to
  788. load DDS files produced by NVidia's Photoshop plugin
  789. }
  790. end.