ImagingDds.pas 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764
  1. {
  2. $Id: ImagingDds.pas,v 1.18 2006/10/26 13:29:28 galfar Exp $
  3. Vampyre Imaging Library
  4. by Marek Mauder ([email protected])
  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.
  37. You can get some information about last loaded DDS file by calling
  38. GetOption with ImagingDDSXXX options and you can set some
  39. saving options by calling SetOption.}
  40. TDDSFileFormat = class(TImageFileFormat)
  41. protected
  42. FLoadedCubeMap: LongBool;
  43. FLoadedVolume: LongBool;
  44. FLoadedMipMapCount: LongInt;
  45. FLoadedDepth: LongInt;
  46. FSaveCubeMap: LongBool;
  47. FSaveVolume: LongBool;
  48. FSaveMipMapCount: LongInt;
  49. FSaveDepth: LongInt;
  50. function GetSupportedFormats: TImageFormats; override;
  51. procedure LoadData(Handle: TImagingHandle; var Images: TDynImageDataArray;
  52. OnlyFirstLevel: Boolean); override;
  53. procedure SaveData(Handle: TImagingHandle; const Images: TDynImageDataArray;
  54. Index: LongInt); override;
  55. function MakeCompatible(const Image: TImageData; var Comp: TImageData): Boolean; override;
  56. public
  57. constructor Create; override;
  58. function TestFormat(Handle: TImagingHandle): Boolean; override;
  59. end;
  60. const
  61. SDDSExtensions = 'dds';
  62. SDDSFormatName = 'DirectDraw Surface';
  63. DDSSupportedFormats: TImageFormats = [ifR8G8B8, ifA8R8G8B8, ifA1R5G5B5,
  64. ifA4R4G4B4, ifX1R5G5B5, ifX4R4G4B4, ifR5G6B5, ifA16B16G16R16, ifR32F,
  65. ifA32B32G32R32F, ifR16F, ifA16B16G16R16F, ifR3G3B2, ifGray8, ifA8Gray8,
  66. ifGray16, ifDXT1, ifDXT3, ifDXT5];
  67. implementation
  68. const
  69. { Four character codes.}
  70. DDSMagic = LongWord(Byte('D') or (Byte('D') shl 8) or (Byte('S') shl 16) or
  71. (Byte(' ') shl 24));
  72. FOURCC_DXT1 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  73. (Byte('1') shl 24));
  74. FOURCC_DXT3 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  75. (Byte('3') shl 24));
  76. FOURCC_DXT5 = LongWord(Byte('D') or (Byte('X') shl 8) or (Byte('T') shl 16) or
  77. (Byte('5') shl 24));
  78. { Some D3DFORMAT values used in DDS files as FourCC value.}
  79. D3DFMT_A16B16G16R16 = 36;
  80. D3DFMT_R32F = 114;
  81. D3DFMT_A32B32G32R32F = 116;
  82. D3DFMT_R16F = 111;
  83. D3DFMT_A16B16G16R16F = 113;
  84. { Constans used by TDDSurfaceDesc2.Flags.}
  85. DDSD_CAPS = $00000001;
  86. DDSD_HEIGHT = $00000002;
  87. DDSD_WIDTH = $00000004;
  88. DDSD_PITCH = $00000008;
  89. DDSD_PIXELFORMAT = $00001000;
  90. DDSD_MIPMAPCOUNT = $00020000;
  91. DDSD_LINEARSIZE = $00080000;
  92. DDSD_DEPTH = $00800000;
  93. { Constans used by TDDSPixelFormat.Flags.}
  94. DDPF_ALPHAPIXELS = $00000001; // used by formats which contain alpha
  95. DDPF_FOURCC = $00000004; // used by DXT and large ARGB formats
  96. DDPF_RGB = $00000040; // used by RGB formats
  97. DDPF_LUMINANCE = $00020000; // used by formats like D3DFMT_L16
  98. DDPF_BUMPLUMINANCE = $00040000; // used by mixed signed-unsigned formats
  99. DDPF_BUMPDUDV = $00080000; // used by signed formats
  100. { Constans used by TDDSCaps.Caps1.}
  101. DDSCAPS_COMPLEX = $00000008;
  102. DDSCAPS_TEXTURE = $00001000;
  103. DDSCAPS_MIPMAP = $00400000;
  104. { Constans used by TDDSCaps.Caps2.}
  105. DDSCAPS2_CUBEMAP = $00000200;
  106. DDSCAPS2_POSITIVEX = $00000400;
  107. DDSCAPS2_NEGATIVEX = $00000800;
  108. DDSCAPS2_POSITIVEY = $00001000;
  109. DDSCAPS2_NEGATIVEY = $00002000;
  110. DDSCAPS2_POSITIVEZ = $00004000;
  111. DDSCAPS2_NEGATIVEZ = $00008000;
  112. DDSCAPS2_VOLUME = $00200000;
  113. { Minimal set of flags required to be in TDDSurfaceDesc2.Flags.}
  114. DDS_REQUIRED_FLAGS = DDSD_CAPS or DDSD_PIXELFORMAT or DDSD_WIDTH or
  115. DDSD_HEIGHT;
  116. { Flags for TDDSurfaceDesc2.Flags used when saving DDS file.}
  117. DDS_SAVE_FLAGS = DDS_REQUIRED_FLAGS or DDSD_LINEARSIZE;
  118. type
  119. { Stores the pixel format information.}
  120. TDDPixelFormat = packed record
  121. Size: LongWord; // Size of the structure = 32 bytes
  122. Flags: LongWord; // Flags to indicate valid fields
  123. FourCC: LongWord; // Four-char code for compressed textures (DXT)
  124. BitCount: LongWord; // Bits per pixel if uncomp. usually 16,24 or 32
  125. RedMask: LongWord; // Bit mask for the Red component
  126. GreenMask: LongWord; // Bit mask for the Green component
  127. BlueMask: LongWord; // Bit mask for the Blue component
  128. AlphaMask: LongWord; // Bit mask for the Alpha component
  129. end;
  130. { Specifies capabilities of surface.}
  131. TDDSCaps = packed record
  132. Caps1: LongWord; // Should always include DDSCAPS_TEXTURE
  133. Caps2: LongWord; // For cubic environment maps
  134. Reserved: array[0..1] of LongWord; // Reserved
  135. end;
  136. { Record describing DDS file contents.}
  137. TDDSurfaceDesc2 = packed record
  138. Size: LongWord; // Size of the structure = 124 Bytes
  139. Flags: LongWord; // Flags to indicate valid fields
  140. Height: LongWord; // Height of the main image in pixels
  141. Width: LongWord; // Width of the main image in pixels
  142. PitchOrLinearSize: LongWord; // For uncomp formats number of bytes per
  143. // scanline. For comp it is the size in
  144. // bytes of the main image
  145. Depth: LongWord; // Only for volume text depth of the volume
  146. MipMaps: LongInt; // Total number of levels in the mipmap chain
  147. Reserved1: array[0..10] of LongWord; // Reserved
  148. PixelFormat: TDDPixelFormat; // Format of the pixel data
  149. Caps: TDDSCaps; // Capabilities
  150. Reserved2: LongWord; // Reserved
  151. end;
  152. { DDS file header.}
  153. TDDSFileHeader = packed record
  154. Magic: LongWord; // File format magic
  155. Desc: TDDSurfaceDesc2; // Surface description
  156. end;
  157. function GetVolumeLevelCount(Depth, MipMaps: LongInt): LongInt;
  158. var
  159. I: LongInt;
  160. begin
  161. Result := Depth;
  162. for I := 1 to MipMaps - 1 do
  163. Inc(Result, ClampInt(Depth shr I, 1, Depth));
  164. end;
  165. { TDDSFileFormat class implementation }
  166. constructor TDDSFileFormat.Create;
  167. begin
  168. inherited Create;
  169. FName := SDDSFormatName;
  170. FCanLoad := True;
  171. FCanSave := True;
  172. FIsMultiImageFormat := True;
  173. FSaveCubeMap := False;
  174. FSaveVolume := False ;
  175. FSaveMipMapCount := 1;
  176. FSaveDepth := 1;
  177. AddExtensions(SDDSExtensions);
  178. RegisterOption(ImagingDDSLoadedCubeMap, @FLoadedCubeMap);
  179. RegisterOption(ImagingDDSLoadedVolume, @FLoadedVolume);
  180. RegisterOption(ImagingDDSLoadedMipMapCount, @FLoadedMipMapCount);
  181. RegisterOption(ImagingDDSLoadedDepth, @FLoadedDepth);
  182. RegisterOption(ImagingDDSSaveCubeMap, @FSaveCubeMap);
  183. RegisterOption(ImagingDDSSaveVolume, @FSaveVolume);
  184. RegisterOption(ImagingDDSSaveMipMapCount, @FSaveMipMapCount);
  185. RegisterOption(ImagingDDSSaveDepth, @FSaveDepth);
  186. end;
  187. function TDDSFileFormat.GetSupportedFormats: TImageFormats;
  188. begin
  189. Result := DDSSupportedFormats;
  190. end;
  191. procedure TDDSFileFormat.LoadData(Handle: TImagingHandle;
  192. var Images: TDynImageDataArray; OnlyFirstLevel: Boolean);
  193. var
  194. Hdr: TDDSFileHeader;
  195. SrcFormat: TImageFormat;
  196. FmtInfo: PImageFormatInfo;
  197. NeedsSwapChannels, HasMipMaps: Boolean;
  198. CurWidth, CurHeight, ImageCount, LoadSize, I, PitchOrLinear: LongInt;
  199. Data: PByte;
  200. UseAsPitch: Boolean;
  201. UseAsLinear: Boolean;
  202. function MasksEqual(const DDPF: TDDPixelFormat; PF: PPixelFormatInfo): Boolean;
  203. begin
  204. Result := (DDPF.AlphaMask = PF.ABitMask) and
  205. (DDPF.RedMask = PF.RBitMask) and (DDPF.GreenMask = PF.GBitMask) and
  206. (DDPF.BlueMask = PF.BBitMask);
  207. end;
  208. procedure ComputeSubDimensions(Idx: LongInt; var CurWidth,
  209. CurHeight: LongInt);
  210. var
  211. I, Last, Shift: LongInt;
  212. begin
  213. CurWidth := Hdr.Desc.Width;
  214. CurHeight := Hdr.Desc.Height;
  215. if HasMipMaps then
  216. begin
  217. if not FLoadedVolume then
  218. begin
  219. // Cube maps are stored like this
  220. // Face 0 mimap 0
  221. // Face 0 mipmap 1
  222. // ...
  223. // Face 1 mipmap 0
  224. // Face 1 mipmap 1
  225. // ...
  226. if FLoadedCubeMap then
  227. Idx := Idx - ((Idx div Hdr.Desc.MipMaps) * Hdr.Desc.MipMaps);
  228. for I := 0 to Idx - 1 do
  229. begin
  230. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  231. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  232. end;
  233. end
  234. else
  235. begin
  236. // Volume textures are stored in DDS files like this:
  237. // Slice 0 mipmap 0
  238. // Slice 1 mipmap 0
  239. // Slice 2 mipmap 0
  240. // Slice 3 mipmap 0
  241. // Slice 0 mipmap 1
  242. // Slice 1 mipmap 1
  243. // Slice 0 mipmap 2
  244. // Slice 0 mipmap 3 ...
  245. Shift := 0;
  246. Last := Hdr.Desc.Depth;
  247. while Idx > Last - 1 do
  248. begin
  249. CurWidth := ClampInt(CurWidth shr 1, 1, CurWidth);
  250. CurHeight := ClampInt(CurHeight shr 1, 1, CurHeight);
  251. if (CurWidth = 1) and (CurHeight = 1) then
  252. Break;
  253. Inc(Shift);
  254. Inc(Last, ClampInt(Hdr.Desc.Depth shr Shift, 1, Hdr.Desc.Depth));
  255. end;
  256. end;
  257. end;
  258. end;
  259. begin
  260. with GetIO, Hdr, Hdr.Desc.PixelFormat do
  261. begin
  262. Read(Handle, @Hdr, SizeOF(Hdr));
  263. {
  264. // set position to the end of the header (for possible future versions
  265. // with larger header)
  266. Seek(Handle, Hdr.Desc.Size + SizeOf(Hdr.Magic) - SizeOf(Hdr),
  267. smFromCurrent);
  268. }
  269. SrcFormat := ifUnknown;
  270. NeedsSwapChannels := False;
  271. // get image data format
  272. if (Flags and DDPF_FOURCC) = DDPF_FOURCC then
  273. // handle FourCC and large ARGB formats
  274. case FourCC of
  275. D3DFMT_A16B16G16R16: SrcFormat := ifA16B16G16R16;
  276. D3DFMT_R32F: SrcFormat := ifR32F;
  277. D3DFMT_A32B32G32R32F: SrcFormat := ifA32B32G32R32F;
  278. D3DFMT_R16F: SrcFormat := ifR16F;
  279. D3DFMT_A16B16G16R16F: SrcFormat := ifA16B16G16R16F;
  280. FOURCC_DXT1: SrcFormat := ifDXT1;
  281. FOURCC_DXT3: SrcFormat := ifDXT3;
  282. FOURCC_DXT5: SrcFormat := ifDXT5;
  283. end
  284. else
  285. if (Flags and DDPF_RGB) = DDPF_RGB then
  286. begin
  287. // handle RGB formats
  288. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  289. // handle RGB with alpha formats
  290. case BitCount of
  291. 16:
  292. begin
  293. if MasksEqual(Desc.PixelFormat,
  294. GetFormatInfo(ifA4R4G4B4).PixelFormat) then
  295. SrcFormat := ifA4R4G4B4;
  296. if MasksEqual(Desc.PixelFormat,
  297. GetFormatInfo(ifA1R5G5B5).PixelFormat) then
  298. SrcFormat := ifA1R5G5B5;
  299. end;
  300. 32:
  301. begin
  302. SrcFormat := ifA8R8G8B8;
  303. if BlueMask = $00FF0000 then
  304. NeedsSwapChannels := True;
  305. end;
  306. end
  307. else
  308. // handle RGB without alpha formats
  309. case BitCount of
  310. 8:
  311. if MasksEqual(Desc.PixelFormat,
  312. GetFormatInfo(ifR3G3B2).PixelFormat) then
  313. SrcFormat := ifR3G3B2;
  314. 16:
  315. begin
  316. if MasksEqual(Desc.PixelFormat,
  317. GetFormatInfo(ifX4R4G4B4).PixelFormat) then
  318. SrcFormat := ifX4R4G4B4;
  319. if MasksEqual(Desc.PixelFormat,
  320. GetFormatInfo(ifX1R5G5B5).PixelFormat) then
  321. SrcFormat := ifX1R5G5B5;
  322. if MasksEqual(Desc.PixelFormat,
  323. GetFormatInfo(ifR5G6B5).PixelFormat) then
  324. SrcFormat := ifR5G6B5;
  325. end;
  326. 24: SrcFormat := ifR8G8B8;
  327. 32:
  328. begin
  329. SrcFormat := ifX8R8G8B8;
  330. if BlueMask = $00FF0000 then
  331. NeedsSwapChannels := True;
  332. end;
  333. end;
  334. end
  335. else
  336. if (Flags and DDPF_LUMINANCE) = DDPF_LUMINANCE then
  337. begin
  338. // handle luminance formats
  339. if (Flags and DDPF_ALPHAPIXELS) = DDPF_ALPHAPIXELS then
  340. begin
  341. // handle luminance with alpha formats
  342. if BitCount = 16 then
  343. SrcFormat := ifA8Gray8;
  344. end
  345. else
  346. // handle luminance without alpha formats
  347. case BitCount of
  348. 8: SrcFormat := ifGray8;
  349. 16: SrcFormat := ifGray16;
  350. end;
  351. end
  352. else
  353. if (Flags and DDPF_BUMPLUMINANCE) = DDPF_BUMPLUMINANCE then
  354. begin
  355. // handle mixed bump-luminance formats like D3DFMT_X8L8V8U8
  356. case BitCount of
  357. 32:
  358. if BlueMask = $00FF0000 then
  359. begin
  360. SrcFormat := ifX8R8G8B8; // D3DFMT_X8L8V8U8
  361. NeedsSwapChannels := True;
  362. end;
  363. end;
  364. end
  365. else
  366. if (Flags and DDPF_BUMPDUDV) = DDPF_BUMPDUDV then
  367. begin
  368. // handle bumpmap formats like D3DFMT_Q8W8V8U8
  369. case BitCount of
  370. 16: SrcFormat := ifA8Gray8; // D3DFMT_V8U8
  371. 32:
  372. if AlphaMask = $FF000000 then
  373. begin
  374. SrcFormat := ifA8R8G8B8; // D3DFMT_Q8W8V8U8
  375. NeedsSwapChannels := True;
  376. end;
  377. 64: SrcFormat := ifA16B16G16R16; // D3DFMT_Q16W16V16U16
  378. end;
  379. end;
  380. // determine number of subimages in file and type of texture stored
  381. ImageCount := 1;
  382. HasMipMaps := False;
  383. FLoadedMipMapCount := 1;
  384. FLoadedDepth := 1;
  385. FLoadedVolume := False;
  386. FLoadedCubeMap := False;
  387. // if DDS format is not supported we will exit with one image
  388. // created in default format. Should we raise some error instead?
  389. if SrcFormat = ifUnknown then
  390. begin
  391. SetLength(Images, 1);
  392. NewImage(Desc.Width, Desc.Height, ifDefault, Images[0]);
  393. Exit;
  394. end;
  395. // file contains mipmaps for each subimage
  396. if ((Desc.Caps.Caps1 and DDSCAPS_MIPMAP) = DDSCAPS_MIPMAP) and
  397. ((Desc.Flags and DDSD_MIPMAPCOUNT) = DDSD_MIPMAPCOUNT) then
  398. begin
  399. HasMipMaps := True;
  400. FLoadedMipMapCount := Desc.MipMaps;
  401. ImageCount := Desc.MipMaps;
  402. end;
  403. // file stores volume texture
  404. if ((Desc.Caps.Caps2 and DDSCAPS2_VOLUME) = DDSCAPS2_VOLUME) and
  405. ((Desc.Flags and DDSD_DEPTH) = DDSD_DEPTH) then
  406. begin
  407. FLoadedVolume := True;
  408. FLoadedDepth := Desc.Depth;
  409. ImageCount := GetVolumeLevelCount(Desc.Depth, ImageCount);
  410. end;
  411. // file stores cube texture
  412. if (Desc.Caps.Caps2 and DDSCAPS2_CUBEMAP) = DDSCAPS2_CUBEMAP then
  413. begin
  414. FLoadedCubeMap := True;
  415. I := 0;
  416. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEX) = DDSCAPS2_POSITIVEX then Inc(I);
  417. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEY) = DDSCAPS2_POSITIVEY then Inc(I);
  418. if (Desc.Caps.Caps2 and DDSCAPS2_POSITIVEZ) = DDSCAPS2_POSITIVEZ then Inc(I);
  419. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEX) = DDSCAPS2_NEGATIVEX then Inc(I);
  420. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEY) = DDSCAPS2_NEGATIVEY then Inc(I);
  421. if (Desc.Caps.Caps2 and DDSCAPS2_NEGATIVEZ) = DDSCAPS2_NEGATIVEZ then Inc(I);
  422. FLoadedDepth := I;
  423. ImageCount := ImageCount * I;
  424. end;
  425. // allocate and load all images in file
  426. FmtInfo := GetFormatInfo(SrcFormat);
  427. { whole image muse be read from input stream to support more images in one stream
  428. if OnlyFirstLevel then
  429. ImageCount := 1;
  430. }
  431. SetLength(Images, ImageCount);
  432. for I := 0 to ImageCount - 1 do
  433. begin
  434. // compute dimensions of surrent subimage based on texture type and
  435. // number of mipmaps
  436. ComputeSubDimensions(I, CurWidth, CurHeight);
  437. NewImage(CurWidth, CurHeight, SrcFormat, Images[I]);
  438. // compute the pitch or get if from file if present
  439. UseAsPitch := (Desc.Flags and DDSD_PITCH) = DDSD_PITCH;
  440. UseAsLinear := (Desc.Flags and DDSD_LINEARSIZE) = DDSD_LINEARSIZE;
  441. if (I = 0) and (UseAsPitch or UseAsLinear) then
  442. PitchOrLinear := Desc.PitchOrLinearSize
  443. else
  444. PitchOrLinear := FmtInfo.GetPixelsSize(SrcFormat, CurWidth, CurHeight);
  445. if UseAsPitch then
  446. LoadSize := CurHeight * PitchOrLinear
  447. else
  448. LoadSize := PitchOrLinear;
  449. if LoadSize = Images[I].Size then
  450. // if DDS does not use Pitch we can simply copy data
  451. Read(Handle, Images[I].Bits, LoadSize)
  452. else
  453. begin
  454. // if DDS uses Pitch we must load aligned scanlines
  455. // and then remove padding
  456. GetMem(Data, LoadSize);
  457. Read(Handle, Data, LoadSize);
  458. RemovePadBytes(Data, Images[I].Bits, CurWidth, CurHeight,
  459. FmtInfo.BytesPerPixel, PitchOrLinear);
  460. FreeMem(Data);
  461. end;
  462. if NeedsSwapChannels then
  463. SwapChannels(Images[I], ChannelRed, ChannelBlue);
  464. end;
  465. end;
  466. end;
  467. procedure TDDSFileFormat.SaveData(Handle: TImagingHandle;
  468. const Images: TDynImageDataArray; Index: Integer);
  469. var
  470. Hdr: TDDSFileHeader;
  471. MainImage, ImageToSave: TImageData;
  472. I, MainIdx, Len, ImageCount: LongInt;
  473. J: LongWord;
  474. FmtInfo: PImageFormatInfo;
  475. begin
  476. Len := Length(Images);
  477. if Len = 0 then Exit;
  478. if (Index = MaxInt) or (Index > Len - 1) then
  479. MainIdx := 0
  480. else
  481. MainIdx := Index;
  482. // check if we have enough images on Input to save cube map
  483. if FSaveCubeMap then
  484. begin
  485. FSaveVolume := False;
  486. if Len - MainIdx < FSaveDepth * FSaveMipMapCount then
  487. FSaveCubeMap := False;
  488. end;
  489. // check if we have enough images on Input to save volume texture
  490. if FSaveVolume then
  491. begin
  492. FSaveCubeMap := False;
  493. if Len - MainIdx < GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount) then
  494. FSaveVolume := False;
  495. end;
  496. // check if we have enough images on Input to save mip maps
  497. if (FSaveMipMapCount > 1) then
  498. begin
  499. if Len - MainIdx < FSaveMipMapCount then
  500. FSaveMipMapCount := 1;
  501. end;
  502. // we create compatible main image and fill headers
  503. if MakeCompatible(Images[MainIdx], MainImage) then
  504. with GetIO, MainImage, Hdr do
  505. try
  506. FmtInfo := GetFormatInfo(Format);
  507. FillChar(Hdr, Sizeof(Hdr), 0);
  508. Magic := DDSMagic;
  509. Desc.Size := SizeOf(Desc);
  510. Desc.Width := Width;
  511. Desc.Height := Height;
  512. Desc.Flags := DDS_SAVE_FLAGS;
  513. Desc.Caps.Caps1 := DDSCAPS_TEXTURE;
  514. Desc.PixelFormat.Size := SizeOf(Desc.PixelFormat);
  515. Desc.PitchOrLinearSize := Images[MainIdx].Size;
  516. ImageCount := FSaveMipMapCount;
  517. if FSaveMipMapCount > 1 then
  518. begin
  519. Desc.Flags := Desc.Flags or DDSD_MIPMAPCOUNT;
  520. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_MIPMAP;
  521. Desc.MipMaps := FSaveMipMapCount;
  522. end;
  523. if FSaveCubeMap then
  524. begin
  525. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  526. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_CUBEMAP;
  527. J := DDSCAPS2_POSITIVEX;
  528. for I := 0 to FSaveDepth - 1 do
  529. begin
  530. Desc.Caps.Caps2 := Desc.Caps.Caps2 or J;
  531. J := J shl 1;
  532. end;
  533. ImageCount := FSaveDepth * FSaveMipMapCount;
  534. end;
  535. if FSaveVolume then
  536. begin
  537. Desc.Flags := Desc.Flags or DDSD_DEPTH;
  538. Desc.Caps.Caps1 := Desc.Caps.Caps1 or DDSCAPS_COMPLEX;
  539. Desc.Caps.Caps2 := Desc.Caps.Caps2 or DDSCAPS2_VOLUME;
  540. Desc.Depth := FSaveDepth;
  541. ImageCount := GetVolumeLevelCount(FSaveDepth, FSaveMipMapCount);
  542. end;
  543. // now we set DDS pixel format for main image
  544. if FmtInfo.IsSpecial or FmtInfo.IsFloatingPoint or
  545. (FmtInfo.BytesPerPixel > 4) then
  546. begin
  547. Desc.PixelFormat.Flags := DDPF_FOURCC;
  548. case Format of
  549. ifA16B16G16R16: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16;
  550. ifR32F: Desc.PixelFormat.FourCC := D3DFMT_R32F;
  551. ifA32B32G32R32F: Desc.PixelFormat.FourCC := D3DFMT_A32B32G32R32F;
  552. ifR16F: Desc.PixelFormat.FourCC := D3DFMT_R16F;
  553. ifA16B16G16R16F: Desc.PixelFormat.FourCC := D3DFMT_A16B16G16R16F;
  554. ifDXT1: Desc.PixelFormat.FourCC := FOURCC_DXT1;
  555. ifDXT3: Desc.PixelFormat.FourCC := FOURCC_DXT3;
  556. ifDXT5: Desc.PixelFormat.FourCC := FOURCC_DXT5;
  557. end;
  558. end
  559. else
  560. if FmtInfo.HasGrayChannel then
  561. begin
  562. Desc.PixelFormat.Flags := DDPF_LUMINANCE;
  563. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  564. case Format of
  565. ifGray8: Desc.PixelFormat.RedMask := 255;
  566. ifGray16: Desc.PixelFormat.RedMask := 65535;
  567. ifA8Gray8:
  568. begin
  569. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or
  570. DDPF_ALPHAPIXELS;
  571. Desc.PixelFormat.RedMask := 255;
  572. Desc.PixelFormat.AlphaMask := 65280;
  573. end;
  574. end;
  575. end
  576. else
  577. begin
  578. Desc.PixelFormat.Flags := DDPF_RGB;
  579. Desc.PixelFormat.BitCount := FmtInfo.BytesPerPixel * 8;
  580. if FmtInfo.HasAlphaChannel then
  581. begin
  582. Desc.PixelFormat.Flags := Desc.PixelFormat.Flags or
  583. DDPF_ALPHAPIXELS;
  584. Desc.PixelFormat.AlphaMask := $FF000000;
  585. end;
  586. if FmtInfo.BytesPerPixel > 2 then
  587. begin
  588. Desc.PixelFormat.RedMask := $00FF0000;
  589. Desc.PixelFormat.GreenMask := $0000FF00;
  590. Desc.PixelFormat.BlueMask := $000000FF;
  591. end
  592. else
  593. begin
  594. Desc.PixelFormat.AlphaMask := FmtInfo.PixelFormat.ABitMask;
  595. Desc.PixelFormat.RedMask := FmtInfo.PixelFormat.RBitMask;
  596. Desc.PixelFormat.GreenMask := FmtInfo.PixelFormat.GBitMask;
  597. Desc.PixelFormat.BlueMask := FmtInfo.PixelFormat.BBitMask;
  598. end;
  599. end;
  600. // header and main image are wriiten to output
  601. Write(Handle, @Hdr, SizeOf(Hdr));
  602. Write(Handle, MainImage.Bits, MainImage.Size);
  603. // write the rest of the images (and convert them to
  604. // the same format as main image if necessary)
  605. for I := MainIdx + 1 to MainIdx + ImageCount - 1 do
  606. begin
  607. if Images[I].Format <> Format then
  608. begin
  609. CloneImage(Images[I], ImageToSave);
  610. ConvertImage(ImageToSave, Format);
  611. end
  612. else
  613. ImageToSave := Images[I];
  614. Write(Handle, ImageToSave.Bits, ImageToSave.Size);
  615. if Images[I].Bits <> ImageToSave.Bits then
  616. FreeImage(ImageToSave);
  617. end;
  618. finally
  619. if Images[MainIdx].Bits <> MainImage.Bits then
  620. FreeImage(MainImage);
  621. end;
  622. end;
  623. function TDDSFileFormat.MakeCompatible(const Image: TImageData;
  624. var Comp: TImageData): Boolean;
  625. var
  626. Info: PImageFormatInfo;
  627. ConvFormat: TImageFormat;
  628. begin
  629. if not inherited MakeCompatible(Image, Comp) then
  630. begin
  631. Info := GetFormatInfo(Comp.Format);
  632. if Info.IsIndexed or Info.IsSpecial then
  633. // convert indexed and unsupported special formatd to A8R8G8B8
  634. ConvFormat := ifA8R8G8B8
  635. else
  636. if Info.IsFloatingPoint then
  637. begin
  638. if Info.Format = ifA16R16G16B16F then
  639. // only swap channels here
  640. ConvFormat := ifA16B16G16R16F
  641. else
  642. // convert other floating point formats to A32B32G32R32F
  643. ConvFormat := ifA32B32G32R32F
  644. end
  645. else
  646. if Info.HasGrayChannel then
  647. begin
  648. if Info.HasAlphaChannel then
  649. // convert grayscale with alpha to A8Gray8
  650. ConvFormat := ifA8Gray8
  651. else
  652. if Info.BytesPerPixel = 1 then
  653. // convert 8bit grayscale to Gray8
  654. ConvFormat := ifGray8
  655. else
  656. // convert 16-64bit grayscales to Gray16
  657. ConvFormat := ifGray16;
  658. end
  659. else
  660. if Info.BytesPerPixel > 4 then
  661. ConvFormat := ifA16B16G16R16
  662. else
  663. if Info.HasAlphaChannel then
  664. // convert the other images with alpha channel to A8R8G8B8
  665. ConvFormat := ifA8R8G8B8
  666. else
  667. // convert the other formats to X8R8G8B8
  668. ConvFormat := ifX8R8G8B8;
  669. ConvertImage(Comp, ConvFormat);
  670. end;
  671. Result := Comp.Format in GetSupportedFormats;
  672. end;
  673. function TDDSFileFormat.TestFormat(Handle: TImagingHandle): Boolean;
  674. var
  675. Hdr: TDDSFileHeader;
  676. ReadCount: LongInt;
  677. begin
  678. Result := False;
  679. if Handle <> nil then
  680. with GetIO do
  681. begin
  682. ReadCount := Read(Handle, @Hdr, SizeOf(Hdr));
  683. Seek(Handle, -ReadCount, smFromCurrent);
  684. Result := (Hdr.Magic = DDSMagic) and (ReadCount = SizeOf(Hdr)) and
  685. ((DDS_REQUIRED_FLAGS and Hdr.Desc.Flags) = DDS_REQUIRED_FLAGS) and
  686. ((Hdr.Desc.Caps.Caps1 and DDSCAPS_TEXTURE) = DDSCAPS_TEXTURE);
  687. end;
  688. end;
  689. initialization
  690. RegisterImageFileFormat(TDDSFileFormat);
  691. {
  692. File Notes:
  693. -- TODOS ----------------------------------------------------
  694. - when saving multi image to DDS make sure all levels are
  695. saved and with proper dims and format
  696. - always store more images if they are on input, not only when
  697. SaveMipMapCount is set (problem in VampConvert)
  698. -- 0.19 Changes/Bug Fixes -----------------------------------
  699. - added support for half-float image formats
  700. - change in LoadData to allow support for more images
  701. in one stream loading
  702. -- 0.17 Changes/Bug Fixes -----------------------------------
  703. - fixed bug in TestFormat which does not recognize many DDS files
  704. - changed pitch/linearsize handling in DDS loading code to
  705. load DDS files produced by NVidia's Photoshop plugin
  706. }
  707. end.