1// tImageDDS.cpp 
2// 
3// This class knows how to load Direct Draw Surface (.dds) files. Saving is not implemented yet. 
4// It does zero processing of image data. It knows the details of the dds file format and loads the data into tLayers. 
5// Currently it does not compress or decompress the image data if it is compressed (DXTn), it simply keeps it in the 
6// same format as the source file. The layers may be 'stolen' from a tImageDDS so that excessive memcpys are avoided. 
7// After they are stolen the tImageDDS is invalid. 
8// 
9// Copyright (c) 2006, 2017, 2019, 2020 Tristan Grimmer. 
10// Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby 
11// granted, provided that the above copyright notice and this permission notice appear in all copies. 
12// 
13// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL 
14// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 
15// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN 
16// AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 
17// PERFORMANCE OF THIS SOFTWARE. 
18 
19#include <Foundation/tString.h> 
20#include "Image/tImageDDS.h" 
21#define STRICT_DDS_HEADER_CHECKING 
22#define FourCC(ch0, ch1, ch2, ch3) (uint(uint8(ch0)) | (uint(uint8(ch1)) << 8) | (uint(uint8(ch2)) << 16) | (uint(uint8(ch3)) << 24)) 
23namespace tImage 
24
25 
26 
27tImageDDS::tImageDDS() : 
28 Filename(), 
29 PixelFormat(tPixelFormat::Invalid), 
30 IsCubeMap(false), 
31 NumImages(0), 
32 NumMipmapLayers(0
33
34 tStd::tMemset(MipmapLayers, 0, sizeof(MipmapLayers)); 
35
36 
37 
38tImageDDS::tImageDDS(const tString& ddsFile, bool reverseRowOrder) : 
39 Filename(ddsFile), 
40 PixelFormat(tPixelFormat::Invalid), 
41 IsCubeMap(false), 
42 NumImages(0), 
43 NumMipmapLayers(0
44
45 tStd::tMemset(MipmapLayers, 0, sizeof(MipmapLayers)); 
46 Load(ddsFile, reverseRowOrder); 
47
48 
49 
50tImageDDS::tImageDDS(const uint8* ddsFileInMemory, int numBytes, bool reverseRowOrder) : 
51 Filename(), 
52 PixelFormat(tPixelFormat::Invalid), 
53 IsCubeMap(false), 
54 NumImages(0), 
55 NumMipmapLayers(0
56
57 tStd::tMemset(MipmapLayers, 0, sizeof(MipmapLayers)); 
58 Load(ddsFileInMemory, numBytes, reverseRowOrder); 
59
60 
61 
62void tImageDDS::Clear() 
63
64 for (int image = 0; image < NumImages; image++) 
65
66 for (int layer = 0; layer < NumMipmapLayers; layer++) 
67
68 delete MipmapLayers[layer][image]; 
69 MipmapLayers[layer][image] = 0
70
71
72 
73 Filename.Clear(); 
74 PixelFormat = tPixelFormat::Invalid
75 IsCubeMap = false
76 NumImages = 0
77 NumMipmapLayers = 0
78
79 
80 
81bool tImageDDS::IsOpaque() const 
82
83 switch (PixelFormat
84
85 case tPixelFormat::R8G8B8A8
86 case tPixelFormat::B8G8R8A8
87 case tPixelFormat::BC1_DXT1BA
88 case tPixelFormat::BC2_DXT3
89 case tPixelFormat::BC3_DXT5
90 case tPixelFormat::G3B5A1R5G2
91 case tPixelFormat::G4B4A4R4
92 return false
93 
94 default
95 return true
96
97 
98 return true
99
100 
101 
102bool tImageDDS::StealTextureLayers(tList<tLayer>& layers
103
104 if (!IsValid() || IsCubemap() || (NumImages <= 0)) 
105 return false
106 
107 for (int mip = 0; mip < NumMipmapLayers; mip++) 
108
109 layers.Append(MipmapLayers[mip][0]); 
110 MipmapLayers[mip][0] = nullptr
111
112 
113 Clear(); 
114 return true
115
116 
117 
118int tImageDDS::StealCubemapLayers(tList<tLayer> layerLists[tSurfIndex_NumSurfaces], uint32 sideFlags
119
120 if (!IsValid() || !IsCubemap() || !sideFlags
121 return 0
122 
123 int sideCount = 0
124 for (int side = 0; side < tSurfIndex_NumSurfaces; side++) 
125
126 uint32 sideFlag = 1 << side
127 if (!(sideFlag & sideFlags)) 
128 continue
129 
130 tList<tLayer>& layers = layerLists[side]; 
131 for (int mip = 0; mip < NumMipmapLayers; mip++) 
132
133 layers.Append( MipmapLayers[mip][side] ); 
134 MipmapLayers[mip][side] = nullptr
135
136 sideCount++; 
137
138 
139 Clear(); 
140 return sideCount
141
142 
143 
144enum tDDSPixelFormatFlag 
145
146 // May be used in the DDSPixelFormat struct to indicate alphas present for RGB formats. 
147 tDDSPixelFormatFlag_Alpha = 0x00000001
148 
149 // A DDS file may contain this type of data (pixel format). eg. DXT1 is a fourCC format. 
150 tDDSPixelFormatFlag_FourCC = 0x00000004
151 
152 // A DDS file may contain this type of data (pixel format). eg. A8R8G8B8 
153 tDDSPixelFormatFlag_RGB = 0x00000040 
154}; 
155 
156 
157#pragma pack(push, 4) 
158struct tDDSPixelFormat 
159
160 // Must be 32. 
161 uint32 Size
162 
163 // See tDDSPixelFormatFlag. Flags to indicate valid fields. Uncompressed formats will usually use 
164 // tDDSPixelFormatFlag_RGB to indicate an RGB format, while compressed formats will use tDDSPixelFormatFlag_FourCC 
165 // with a four-character code. 
166 uint32 Flags
167 
168 // "DXT1", "DXT3", and "DXT5" are examples. m_flags should have DDSPixelFormatFlag_FourCC. 
169 uint32 FourCC
170 
171 // Valid if flags has DDSPixelFormatFlag_RGB. For RGB formats this is the total number of bits per pixel. This 
172 // value is usually 16, 24, or 32. For A8R8G8B8, this value would be 32. 
173 uint32 RGBBitCount
174 
175 // For RGB formats these three fields contain the masks for the red, green, and blue channels. For A8R8G8B8 these 
176 // values would be 0x00FF0000, 0x0000FF00, and 0x000000FF respectively. 
177 uint32 MaskRed
178 uint32 MaskGreen
179 uint32 MaskBlue
180 
181 // If the flags have DDSPixelFormatFlag_Alpha set, this is valid and contains tha alpha mask. Eg. For A8R8G8B8 this 
182 // value would be 0xFF000000. 
183 uint32 MaskAlpha
184}; 
185#pragma pack(pop) 
186 
187 
188enum tDDSCapsBasic 
189
190 tDDSCapsBasic_Complex = 0x00000008
191 tDDSCapsBasic_Texture = 0x00001000
192 tDDSCapsBasic_Mipmap = 0x00400000 
193}; 
194 
195 
196enum tDDSCapsExtra 
197
198 tDDSCapsExtra_CubeMap = 0x00000200
199 tDDSCapsExtra_CubeMapPosX = 0x00000400
200 tDDSCapsExtra_CubeMapNegX = 0x00000800
201 tDDSCapsExtra_CubeMapPosY = 0x00001000
202 tDDSCapsExtra_CubeMapNegY = 0x00002000
203 tDDSCapsExtra_CubeMapPosZ = 0x00004000
204 tDDSCapsExtra_CubeMapNegZ = 0x00008000
205 tDDSCapsExtra_Volume = 0x00200000 
206}; 
207 
208 
209#pragma pack(push, 4) 
210struct tDDSCapabilities 
211
212 // DDS files should always include tDDSCapsBasic_Texture. If the file contains mipmaps tDDSCapsBasic_Mipmap should 
213 // be set. For any dds file with more than one main surface, such as a mipmap, cubic environment map, or volume 
214 // texture, DDSCapsBasic_Complex should also be set. 
215 uint32 FlagsCapsBasic
216 
217 // For cubic environment maps tDDSCapsExtra_CubeMap should be included as well as one or more faces of the map 
218 // (tDDSCapsExtra_CubeMapPosX, etc). For volume textures tDDSCapsExtra_Volume should be included. 
219 uint32 FlagsCapsExtra
220 uint32 Unused[2]; 
221}; 
222#pragma pack(pop) 
223 
224 
225enum tDDSFlag 
226
227 tDDSFlag_Caps = 0x00000001, // Always included. 
228 tDDSFlag_Height = 0x00000002, // Always included. Height of largest image if mipmaps included. 
229 tDDSFlag_Width = 0x00000004, // Always included. Width of largest image if mipmaps included. 
230 tDDSFlag_Pitch = 0x00000008
231 tDDSFlag_PixelFormat = 0x00001000, // Always included. 
232 tDDSFlag_MipmapCount = 0x00020000
233 tDDSFlag_LinearSize = 0x00080000
234 tDDSFlag_Depth = 0x00800000 
235}; 
236 
237 
238enum tD3DFORMAT 
239
240 tD3DFMT_UNKNOWN = 0
241 
242 tD3DFMT_R8G8B8 = 20
243 tD3DFMT_A8R8G8B8 = 21
244 tD3DFMT_X8R8G8B8 = 22
245 tD3DFMT_R5G6B5 = 23
246 tD3DFMT_X1R5G5B5 = 24
247 tD3DFMT_A1R5G5B5 = 25
248 tD3DFMT_A4R4G4B4 = 26
249 tD3DFMT_R3G3B2 = 27
250 tD3DFMT_A8 = 28
251 tD3DFMT_A8R3G3B2 = 29
252 tD3DFMT_X4R4G4B4 = 30
253 tD3DFMT_A2B10G10R10 = 31
254 tD3DFMT_A8B8G8R8 = 32
255 tD3DFMT_X8B8G8R8 = 33
256 tD3DFMT_G16R16 = 34
257 tD3DFMT_A2R10G10B10 = 35
258 tD3DFMT_A16B16G16R16 = 36
259 
260 tD3DFMT_A8P8 = 40
261 tD3DFMT_P8 = 41
262 
263 tD3DFMT_L8 = 50
264 tD3DFMT_A8L8 = 51
265 tD3DFMT_A4L4 = 52
266 
267 tD3DFMT_V8U8 = 60
268 tD3DFMT_L6V5U5 = 61
269 tD3DFMT_X8L8V8U8 = 62
270 tD3DFMT_Q8W8V8U8 = 63
271 tD3DFMT_V16U16 = 64
272 tD3DFMT_A2W10V10U10 = 67
273 
274 tD3DFMT_UYVY = FourCC('U', 'Y', 'V', 'Y'), 
275 tD3DFMT_R8G8_B8G8 = FourCC('R', 'G', 'B', 'G'), 
276 tD3DFMT_YUY2 = FourCC('Y', 'U', 'Y', '2'), 
277 tD3DFMT_G8R8_G8B8 = FourCC('G', 'R', 'G', 'B'), 
278 tD3DFMT_DXT1 = FourCC('D', 'X', 'T', '1'), 
279 tD3DFMT_DXT2 = FourCC('D', 'X', 'T', '2'), 
280 tD3DFMT_DXT3 = FourCC('D', 'X', 'T', '3'), 
281 tD3DFMT_DXT4 = FourCC('D', 'X', 'T', '4'), 
282 tD3DFMT_DXT5 = FourCC('D', 'X', 'T', '5'), 
283 
284 tD3DFMT_D16_LOCKABLE = 70
285 tD3DFMT_D32 = 71
286 tD3DFMT_D15S1 = 73
287 tD3DFMT_D24S8 = 75
288 tD3DFMT_D24X8 = 77
289 tD3DFMT_D24X4S4 = 79
290 tD3DFMT_D16 = 80
291 
292 tD3DFMT_D32F_LOCKABLE = 82
293 tD3DFMT_D24FS8 = 83
294 
295 tD3DFMT_D32_LOCKABLE = 84
296 tD3DFMT_S8_LOCKABLE = 85
297 
298 tD3DFMT_L16 = 81
299 
300 tD3DFMT_VERTEXDATA = 100
301 tD3DFMT_INDEX16 = 101
302 tD3DFMT_INDEX32 = 102
303 
304 tD3DFMT_Q16W16V16U16 = 110
305 
306 tD3DFMT_MULTI2_ARGB8 = FourCC('M','E','T','1'), 
307 
308 tD3DFMT_R16F = 111
309 tD3DFMT_G16R16F = 112
310 tD3DFMT_A16B16G16R16F = 113
311 
312 tD3DFMT_R32F = 114
313 tD3DFMT_G32R32F = 115
314 tD3DFMT_A32B32G32R32F = 116
315 
316 tD3DFMT_CxV8U8 = 117
317 
318 tD3DFMT_FORCE_DWORD = 0x7fffffff 
319}; 
320 
321 
322// Default packing is 8 bytes but the header is 128 bytes (mult of 4), so we make it all work here. 
323#pragma pack(push, 4) 
324struct tDDSHeader 
325
326 uint32 Size; // Must be set to 124. 
327 uint32 Flags; // See tDDSFlags. 
328 uint32 Height; // Height of main image. 
329 uint32 Width; // Width of main image. 
330 
331 // For uncompressed formats, this is the number of bytes per scan line (32-bit aligned) for the main image. dwFlags 
332 // should include DDSD_PITCH in this case. For compressed formats, this is the total number of bytes for the main 
333 // image. m_flags should have tDDSFlag_LinearSize in this case. 
334 uint32 PitchLinearSize
335 uint32 Depth; // For volume textures. tDDSFlag_Depth is set for this to be valid. 
336 uint32 MipmapCount; // Valid if tDDSFlag_MipmapCount set. @todo Count includes main image? 
337 uint32 UnusedA[11]; 
338 tDDSPixelFormat PixelFormat; // 32 Bytes. 
339 tDDSCapabilities Capabilities; // 16 Bytes. 
340 uint32 UnusedB
341}; 
342#pragma pack(pop) 
343 
344 
345// These DXT blocks are needed so that the tImageDDS class can re-order the rows by messing with each block's lookup 
346// table and alpha tables. This is because DDS files have the rows of their textures upside down (texture origin in 
347// OpenGL is lower left, while in DirectX it is upper left). See: http://en.wikipedia.org/wiki/S3_Texture_Compression 
348#pragma pack(push, 1) 
349 
350 
351// This block is used for both DXT1 and DXT1 with binary alpha. It's also used as the colour information block in the 
352// DXT 2, 3, 4 and 5 formats. Size is 64 bits. 
353struct tDXT1Block 
354
355 uint16 Colour0; // R5G6B5 
356 uint16 Colour1; // R5G6B5 
357 uint8 LookupTableRows[4]; 
358}; 
359 
360 
361// This one is the same for DXT2 and 3, although we don't support 2 (premultiplied alpha). Size is 128 bits. 
362struct tDXT3Block 
363
364 uint16 AlphaTableRows[4]; // Each alpha is 4 bits. 
365 tDXT1Block ColourBlock
366}; 
367 
368 
369// This one is the same for DXT4 and 5, although we don't support 4 (premultiplied alpha). Size is 128 bits. 
370struct tDXT5Block 
371
372 uint8 Alpha0
373 uint8 Alpha1
374 uint8 AlphaTable[6]; // Each of the 4x4 pixel entries is 3 bits. 
375 tDXT1Block ColourBlock
376 
377 // These accessors are needed because of the unusual alignment of the 3bit alpha indexes. They each return or set a 
378 // value in [0, 2^12) which represents a single row. The row variable should be in [0, 3] 
379 uint16 GetAlphaRow(int row
380
381 tAssert(row < 4); 
382 switch (row
383
384 case 1
385 return (AlphaTable[2] << 4) | (0x0F & (AlphaTable[1] >> 4)); 
386 
387 case 0
388 return ((AlphaTable[1] & 0x0F) << 8) | AlphaTable[0]; 
389 
390 case 3
391 return (AlphaTable[5] << 4) | (0x0F & (AlphaTable[4] >> 4)); 
392 
393 case 2
394 return ((AlphaTable[4] & 0x0F) << 8) | AlphaTable[3]; 
395
396 return 0
397
398 
399 void SetAlphaRow(int row, uint16 val
400
401 tAssert(row < 4); 
402 tAssert(val < 4096); 
403 switch (row
404
405 case 1
406 AlphaTable[2] = val >> 4
407 AlphaTable[1] = (AlphaTable[1] & 0x0F) | ((val & 0x000F) << 4); 
408 break
409 
410 case 0
411 AlphaTable[1] = (AlphaTable[1] & 0xF0) | (val >> 8); 
412 AlphaTable[0] = val & 0x00FF
413 break
414 
415 case 3
416 AlphaTable[5] = val >> 4
417 AlphaTable[4] = (AlphaTable[4] & 0x0F) | ((val & 0x000F) << 4); 
418 break
419 
420 case 2
421 AlphaTable[4] = (AlphaTable[4] & 0xF0) | (val >> 8); 
422 AlphaTable[3] = val & 0x00FF
423 break
424
425
426}; 
427#pragma pack(pop) 
428 
429 
430void tImageDDS::Load(const tString& ddsFile, bool reverseRowOrder
431
432 Clear(); 
433 if (tSystem::tGetFileType(ddsFile) != tSystem::tFileType::DDS
434 throw tDDSError(tDDSError::tCode::IncorrectExtension, tSystem::tGetFileName(ddsFile)); 
435 
436 if (!tSystem::tFileExists(ddsFile)) 
437 throw tDDSError(tDDSError::tCode::FileNonexistent, tSystem::tGetFileName(ddsFile)); 
438 
439 int ddsSizeBytes
440 uint8* ddsData = (uint8*)tSystem::tLoadFile(ddsFile, 0, &ddsSizeBytes); 
441 LoadFromMemory(ddsData, ddsSizeBytes, reverseRowOrder); 
442 
443 delete[] ddsData
444
445 
446 
447void tImageDDS::Load(const uint8* ddsFileInMemory, int ddsSizeBytes, bool reverseRowOrder
448
449 Clear(); 
450 LoadFromMemory(ddsFileInMemory, ddsSizeBytes, reverseRowOrder); 
451
452 
453 
454void tImageDDS::LoadFromMemory(const uint8* ddsData, int ddsSizeBytes, bool reverseRowOrder
455
456 tString baseName = tSystem::tGetFileName(Filename); 
457 
458 // This will deal with zero-sized files properly as well. 
459 if (ddsSizeBytes < int(sizeof(tDDSHeader)+4)) 
460
461 delete[] ddsData
462 throw tDDSError(tDDSError::tCode::IncorrectFileSize, baseName); 
463
464 
465 const uint8* ddsCurr = ddsData
466 uint32& magic = *((uint32*)ddsCurr); ddsCurr += sizeof(uint32); 
467 
468 if (magic != ' SDD'
469
470 delete[] ddsData
471 throw tDDSError(tDDSError::tCode::Magic); 
472
473 
474 tDDSHeader& header = *((tDDSHeader*)ddsCurr); ddsCurr += sizeof(header); 
475 tAssert(sizeof(tDDSHeader) == 124); 
476 const uint8* pixelData = ddsCurr
477 
478 if (header.Size != 124
479
480 delete[] ddsData
481 throw tDDSError(tDDSError::tCode::IncorrectHeaderSize, baseName); 
482
483 
484 uint32 flags = header.Flags
485 int mainWidth = header.Width; // Main image. 
486 int mainHeight = header.Height; // Main image. 
487 
488 if (!tMath::tIsPower2(mainWidth) || !tMath::tIsPower2(mainHeight)) 
489
490 delete[] ddsData
491 throw tDDSError(tDDSError::tCode::LoaderSupportsPowerOfTwoDimsOnly, baseName); 
492
493 
494 // It seems ATI tools like GenCubeMap don't set the correct bits. 
495 #ifdef STRICT_DDS_HEADER_CHECKING 
496 int pitch = 0; // Num bytes per line on main image (uncompressed images only). 
497 int linearSize = 0; // Num bytes total main image (compressed images only). 
498 
499 if (flags & tDDSFlag_Pitch
500 pitch = header.PitchLinearSize
501 
502 if (flags & tDDSFlag_LinearSize
503 linearSize = header.PitchLinearSize
504 
505 // Linear size xor pitch must be specified. 
506 if ((!linearSize && !pitch) || (linearSize && pitch)) 
507
508 delete[] ddsData
509 throw tDDSError(tDDSError::tCode::PitchOrLinearSize, baseName); 
510
511 #endif 
512 
513 // Volume textures are not supported. 
514 if (flags & tDDSFlag_Depth
515
516 delete[] ddsData
517 throw tDDSError(tDDSError::tCode::VolumeTexturesNotSupported, baseName); 
518
519 
520 // Determine the expected number of layers by looking at the mipmap count if it is supplied. We assume a single layer 
521 // if it's not specified. 
522 NumMipmapLayers = 1
523 bool hasMipmaps = (header.Capabilities.FlagsCapsBasic & tDDSCapsBasic_Mipmap) ? true : false
524 if ((flags & tDDSFlag_MipmapCount) && hasMipmaps
525 NumMipmapLayers = header.MipmapCount
526 
527 if (NumMipmapLayers > MaxMipmapLayers
528
529 delete[] ddsData
530 throw tDDSError(tDDSError::tCode::MaxNumMipmapLevelsExceeded); 
531
532 
533 // Determine if this is a cubemap dds with 6 images. No need to check which images are present since they are 
534 // required to be all there by the dds standard. All tools these days seem to write them all. If there are complaints 
535 // when using legacy files we can fix this. 
536 if (header.Capabilities.FlagsCapsExtra & tDDSCapsExtra_CubeMap
537
538 IsCubeMap = true
539 NumImages = 6
540
541 else 
542
543 IsCubeMap = false
544 NumImages = 1
545
546 
547 // Determine if we support the pixel format and which one it is. 
548 PixelFormat = tPixelFormat::Invalid
549 tDDSPixelFormat& format = header.PixelFormat
550 
551 if (format.Size != 32
552
553 delete[] ddsData
554 throw tDDSError(tDDSError::tCode::IncorrectPixelFormatSize, baseName); 
555
556 
557 // Has alpha should be true if the pixel format is uncompressed (RGB) and there is an alpha channel. 
558 bool rgbHasAlpha = (format.Flags & tDDSPixelFormatFlag_Alpha) ? true : false
559 bool rgbFormat = (format.Flags & tDDSPixelFormatFlag_RGB) ? true : false
560 bool fourCCFormat = (format.Flags & tDDSPixelFormatFlag_FourCC) ? true : false
561 
562 if ((!rgbFormat && !fourCCFormat) || (rgbFormat && fourCCFormat)) 
563
564 delete[] ddsData
565 throw tDDSError(tDDSError::tCode::InconsistentPixelFormat, baseName); 
566
567 
568 if (fourCCFormat
569
570 switch (format.FourCC
571
572 case FourCC('D','X','T','1'): 
573 // Note that during inspecition of the individual layer data, the DXT1 pixel format might be modified 
574 // to DXT1BA (binary alpha). 
575 PixelFormat = tPixelFormat::BC1_DXT1
576 break
577 
578 case FourCC('D','X','T','3'): 
579 PixelFormat = tPixelFormat::BC2_DXT3
580 break
581 
582 case FourCC('D','X','T','5'): 
583 PixelFormat = tPixelFormat::BC3_DXT5
584 break
585 
586 case tD3DFMT_R32F
587 PixelFormat = tPixelFormat::R32F
588 break
589 
590 case tD3DFMT_G32R32F
591 PixelFormat = tPixelFormat::G32R32F
592 break
593 
594 case tD3DFMT_A32B32G32R32F
595 PixelFormat = tPixelFormat::A32B32G32R32F
596 break
597 
598 case FourCC('D','X','1','0'): 
599 default
600 delete[] ddsData
601 throw tDDSError(tDDSError::tCode::UnsupportedFourCCPixelFormat, baseName); 
602
603
604 
605 // It must be an RGB format. 
606 else 
607
608 // Remember this is a little endian machine, so the masks are lying. Eg. 0xFF0000 in memory is 00 00 FF, so the red 
609 // is last. 
610 switch (format.RGBBitCount
611
612 case 16
613 // Supports G3B5A1R5G2, G4B4A4R4, and G3B5R5G3. 
614 if 
615
616 rgbHasAlpha && 
617 (format.MaskAlpha == 0x8000) && 
618 (format.MaskRed == 0x7C00) && 
619 (format.MaskGreen == 0x03E0) && 
620 (format.MaskBlue == 0x001F
621
622
623 PixelFormat = tPixelFormat::G3B5A1R5G2
624
625 
626 else if 
627
628 rgbHasAlpha && 
629 (format.MaskAlpha == 0xF000) && 
630 (format.MaskRed == 0x0F00) && 
631 (format.MaskGreen == 0x00F0) && 
632 (format.MaskBlue == 0x000F
633
634
635 PixelFormat = tPixelFormat::G4B4A4R4
636
637 
638 else if 
639
640 !rgbHasAlpha && 
641 (format.MaskRed == 0xF800) && 
642 (format.MaskGreen == 0x07E0) && 
643 (format.MaskBlue == 0x001F
644
645
646 PixelFormat = tPixelFormat::G3B5R5G3
647
648 
649 else 
650
651 delete[] ddsData
652 throw tDDSError(tDDSError::tCode::UnsupportedRGBPixelFormat, baseName); 
653
654 
655 break
656 
657 case 24
658 // Supports B8G8R8. 
659 if 
660
661 !rgbHasAlpha && 
662 (format.MaskRed == 0xFF0000) && 
663 (format.MaskGreen == 0x00FF00) && 
664 (format.MaskBlue == 0x0000FF
665
666
667 PixelFormat = tPixelFormat::B8G8R8
668
669 
670 else 
671
672 delete[] ddsData
673 throw tDDSError(tDDSError::tCode::UnsupportedRGBPixelFormat, baseName); 
674
675 
676 break
677 
678 case 32
679 // Supports B8G8R8A8. This is a little endian machine so the masks are lying. 0xFF000000 in memory is 
680 // 00 00 00 FF with alpha last. 
681 if 
682
683 rgbHasAlpha && 
684 (format.MaskAlpha == 0xFF000000) && 
685 (format.MaskRed == 0x00FF0000) && 
686 (format.MaskGreen == 0x0000FF00) && 
687 (format.MaskBlue == 0x000000FF
688
689
690 PixelFormat = tPixelFormat::B8G8R8A8
691
692 else 
693
694 delete[] ddsData
695 throw tDDSError(tDDSError::tCode::UnsupportedRGBPixelFormat, baseName); 
696
697 break
698 
699 default
700 delete[] ddsData
701 throw tDDSError(tDDSError::tCode::UnsupportedRGBPixelFormat, baseName); 
702
703
704 
705 // @todo We do not yet support these formats. 
706 if ((PixelFormat == tPixelFormat::R32F) || (PixelFormat == tPixelFormat::G32R32F) || (PixelFormat == tPixelFormat::A32B32G32R32F)) 
707
708 delete[] ddsData
709 throw tDDSError(tDDSError::tCode::UnsupportedFourCCPixelFormat, baseName); 
710
711 
712 tAssert(PixelFormat != tPixelFormat::Invalid); 
713 if (!rgbFormat && ((mainWidth%4) || (mainHeight%4))) 
714
715 delete[] ddsData
716 throw tDDSError(tDDSError::tCode::UnsupportedDXTDimensions, baseName); 
717
718 
719 for (int image = 0; image < NumImages; image++) 
720
721 int width = mainWidth
722 int height = mainHeight
723 
724 for (int layer = 0; layer < NumMipmapLayers; layer++) 
725
726 int numBytes
727 if (rgbFormat
728
729 numBytes = width*height*format.RGBBitCount/8
730 
731 // Deal with the reverseRowOrder for these RGB formats as well. 
732 if (reverseRowOrder
733
734 uint8* reversedPixelData = new uint8[numBytes]; 
735 uint8* dstData = reversedPixelData
736 
737 // We only support pixel formats that contain a whole number of bytes per pixel. That will cover 
738 // all reasonable formats. 
739 int bytesPerPixel = format.RGBBitCount/8
740 
741 for (int row = height-1; row >= 0; row--) 
742
743 for (int col = 0; col < width; col++) 
744
745 const uint8* srcData = pixelData + row*bytesPerPixel*width + col*bytesPerPixel
746 for (int byte = 0; byte < bytesPerPixel; byte++, dstData++, srcData++) 
747 *dstData = *srcData
748
749
750 
751 // We can simply get the layer to steal the memory (the last true arg). 
752 MipmapLayers[layer][image] = new tLayer(PixelFormat, width, height, reversedPixelData, true); 
753
754 else 
755
756 MipmapLayers[layer][image] = new tLayer(PixelFormat, width, height, (uint8*)pixelData); 
757
758 tAssert(MipmapLayers[layer][image]->GetDataSize() == numBytes); 
759
760 else 
761
762 // Otherwise it's a FourCC DXTn format. Each block encodes a 4x4 square of pixels. DXT2,3,4,5 use 128 
763 // bits per block. DXT1 and DXT1BA use 64bits per block. 
764 int dxtBlockSize = 16
765 if ((PixelFormat == tPixelFormat::BC1_DXT1BA) || (PixelFormat == tPixelFormat::BC1_DXT1)) 
766 dxtBlockSize = 8
767 
768 int numBlocks = tMath::tMax(1, width/4) * tMath::tMax(1, height/4); 
769 numBytes = numBlocks * dxtBlockSize
770 
771 // Here's where we possibly modify the opaque DXT1 texture to be DXT1BA if there are blocks with binary 
772 // transparency. We only bother checking the main layer. If it's opaque we assume all the others are too. 
773 if ((layer == 0) && (PixelFormat == tPixelFormat::BC1_DXT1) && DoDXT1BlocksHaveBinaryAlpha((tDXT1Block*)pixelData, numBlocks)) 
774 PixelFormat = tPixelFormat::BC1_DXT1BA
775 
776 // DDS files store textures upside down. In the OpenGL RH coord system, the lower left of the texture 
777 // is the origin and consecutive rows go up. For this reason we need to read each row of blocks from 
778 // the top to the bottom row. We also need to flip the rows within the 4x4 block by flipping the lookup 
779 // tables. This should be fairly fast as there is no encoding or encoding going on. Width and height 
780 // will go down to 1x1, which will still use a 4x4 DXT pixel-block. 
781 if (reverseRowOrder
782
783 int heightBlocks = height / 4
784 if (height % 4
785 heightBlocks++; 
786 
787 int widthBlocks = width / 4
788 if (width % 4
789 widthBlocks++; 
790 
791 uint8* reversedPixelData = new uint8[numBytes]; 
792 uint8* dstData = reversedPixelData
793 
794 for (int row = heightBlocks-1; row >= 0; row--) 
795
796 for (int col = 0; col < widthBlocks; col++) 
797
798 const uint8* srcData = pixelData + row*dxtBlockSize*widthBlocks + col*dxtBlockSize
799 for (int byte = 0; byte < dxtBlockSize; byte++, dstData++, srcData++) 
800 *dstData = *srcData
801
802
803 
804 // Now we flip the inter-block rows by messing with the block's lookup-table. We need to handle all 
805 // three types of blocks: 1) DXT1, DXT1BA 2) DXT2, DXT3 3) DXT4, DXT5 
806 int totalBlocks = widthBlocks * heightBlocks
807 
808 switch (PixelFormat
809
810 case tPixelFormat::BC1_DXT1BA
811 case tPixelFormat::BC1_DXT1
812
813 tDXT1Block* block = (tDXT1Block*)reversedPixelData
814 for (int b = 0; b < totalBlocks; b++, block++) 
815
816 // Reorder each row's colour indexes. 
817 tStd::tSwap(block->LookupTableRows[0], block->LookupTableRows[3]); 
818 tStd::tSwap(block->LookupTableRows[1], block->LookupTableRows[2]); 
819
820 break
821
822 
823 case tPixelFormat::BC2_DXT3
824
825 tDXT3Block* block = (tDXT3Block*)reversedPixelData
826 for (int b = 0; b < totalBlocks; b++, block++) 
827
828 // Reorder the explicit alphas AND the colour indexes. 
829 tStd::tSwap(block->AlphaTableRows[0], block->AlphaTableRows[3]); 
830 tStd::tSwap(block->AlphaTableRows[1], block->AlphaTableRows[2]); 
831 tStd::tSwap(block->ColourBlock.LookupTableRows[0], block->ColourBlock.LookupTableRows[3]); 
832 tStd::tSwap(block->ColourBlock.LookupTableRows[1], block->ColourBlock.LookupTableRows[2]); 
833
834 break
835
836 
837 case tPixelFormat::BC3_DXT5
838
839 tDXT5Block* block = (tDXT5Block*)reversedPixelData
840 for (int b = 0; b < totalBlocks; b++, block++) 
841
842 // Reorder the alpha indexes AND the colour indexes. 
843 uint16 orig0 = block->GetAlphaRow(0); 
844 block->SetAlphaRow(0, block->GetAlphaRow(3)); 
845 block->SetAlphaRow(3, orig0); 
846 
847 uint16 orig1 = block->GetAlphaRow(1); 
848 block->SetAlphaRow(1, block->GetAlphaRow(2)); 
849 block->SetAlphaRow(2, orig1); 
850 
851 tStd::tSwap(block->ColourBlock.LookupTableRows[0], block->ColourBlock.LookupTableRows[3]); 
852 tStd::tSwap(block->ColourBlock.LookupTableRows[1], block->ColourBlock.LookupTableRows[2]); 
853
854 break
855
856 
857 case tPixelFormat::R32F
858 case tPixelFormat::G32R32F
859 case tPixelFormat::A32B32G32R32F
860
861 delete[] ddsData
862 throw tDDSError(tDDSError::tCode::UnsuportedFloatingPointPixelFormat, baseName); 
863
864 
865 default
866
867 delete[] ddsData
868 throw tDDSError(tDDSError::tCode::UnsupportedFourCCPixelFormat, baseName); 
869
870
871 
872 // Finally we can append a layer with the massaged dxt data. We can simply get the layer to steal the memory (the 
873 // last true arg). 
874 MipmapLayers[layer][image] = new tLayer(PixelFormat, width, height, reversedPixelData, true); 
875
876 else 
877
878 // If reverseRowOrder is false we want the data to go straight in so we use the pixelData directly. 
879 MipmapLayers[layer][image] = new tLayer(PixelFormat, width, height, (uint8*)pixelData); 
880
881 tAssert(MipmapLayers[layer][image]->GetDataSize() == numBytes); 
882
883 
884 pixelData += numBytes
885 
886 // @todo Does this assume power-of-2 dimensions? Can we avoid this assumption in this low-level class? 
887 width /= 2
888 if (width < 1
889 width = 1
890 
891 height /= 2
892 if (height < 1
893 height = 1
894
895
896
897 
898 
899bool tImageDDS::DoDXT1BlocksHaveBinaryAlpha(tDXT1Block* block, int numBlocks
900
901 // The only way to check if the DXT1 format has alpha is by checking each block individually. If the block uses 
902 // alpha, the min and max colours are ordered in a particular order. 
903 for (int b = 0; b < numBlocks; b++) 
904
905 if (block->Colour0 <= block->Colour1
906
907 // OK, well, that's annoying. It seems that at least the nVidia DXT compressor can generate an opaque DXT1 
908 // block with the colours in the order for a transparent one. This forces us to check all the indexes to 
909 // see if the alpha index (11 in binary) is used -- if not then it's still an opaque block. 
910 for (int row = 0; row < 4; row++) 
911
912 uint8 bits = block->LookupTableRows[row]; 
913 if 
914
915 ((bits & 0x03) == 0x03) || 
916 ((bits & 0x0C) == 0x0C) || 
917 ((bits & 0x30) == 0x30) || 
918 ((bits & 0xC0) == 0xC0
919
920 return true
921
922
923 
924 block++; 
925
926 
927 return false
928
929 
930 
931
932 
933 
934const char* tDDSError::CodeStrings[int(tCode::NumCodes)] = 
935
936 "Unknown."
937 "File doesn't exist."
938 "Incorrect DDS extension."
939 "Filesize incorrect."
940 "Magic FourCC Incorrect."
941 "Incorrect DDS header size."
942 "One of Pitch or LinearSize must be specified."
943 "Volume textures unsupported."
944 "Pixel format size incorrect."
945 "Pixel format must be either an RGB format or a FourCC format."
946 "Unsupported FourCC pixel format. Supported FourCC formats include DXT1, DXT3, DXT5."
947 "Unsupported RGB pixel format. Supported formats include A1R5G5B5, A4R4G4B4, R5G6B5, R8G8B8, and A8R8G8B8."
948 "Incorrect DXT pixel data size."
949 "DXT Texture dimensions must be divisible by 4."
950 "Current DDS loader only supports power-of-2 dimensions."
951 "Maximum number of mipmap levels exceeded."
952 "Floating point pixel formats not supported yet." 
953}; 
954