DEADSOFTWARE

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