1// tImageDDS.h 
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#pragma once 
20#include <System/tThrow.h> 
21#include "Image/tLayer.h" 
22#include "Image/tPixelFormat.h" 
23namespace tImage 
24
25 
26 
27struct tDXT1Block
28 
29 
30// A tImageDDS object represents and knows how to load a dds file. In general a DirectDrawSurface is composed of 
31// multiple layers -- each one a mipmap. This class does not compress or decompress the image data if it is compressed 
32// (DXTn), it simply keeps it in the same format as the source file. The layers may be 'stolen' from a tImageDDS so that 
33// excessive memcpys are avoided. 
34class tImageDDS 
35
36public
37 // Creates an invalid tImageDDS. You must call Load manually. 
38 tImageDDS(); 
39 
40 // If the dds file fails to load it will throw an tDDSError. If nothing is thrown, the object is guaranteed to be 
41 // valid. The reverse row order flag allows this class to reverse the order of the rows from how thay are stored in 
42 // a dds file. OpenGL uses the lower left of the texture as the origin, while DirectX uses the upper left. Use this 
43 // set to true for OpenGL style textures. This flag does _not_ cause a decompress/recompress pass even for BC 
44 // compressed dds files. BC blocks can be massaged without decompression to fix the row order -- and that is what 
45 // this class does. 
46 tImageDDS(const tString& ddsFile, bool correctRowOrder = true); 
47 
48 // This load from memory constructor behaves a lot like the from-file version. The file image in memory is copied 
49 // from and the owner may delete it immediately after if desired. 
50 tImageDDS(const uint8* ddsFileInMemory, int numBytes, bool correctRowOrder = true); 
51 virtual ~tImageDDS() { Clear(); } 
52 
53 // Clears the current tImageDDS before loading. If the dds file failed to load for any reason it will throw a 
54 // tDDSError that indicates the problem. A dds may fail to load for a number of reasons: Volume textures are not 
55 // supported, some pixel-formats may not yet be supported, or inconsistent flags. 
56 void Load(const tString& ddsFile, bool reverseRowOrder = true); 
57 void Load(const uint8* ddsFileInMemory, int numBytes, bool reverseRowOrder = true); 
58 
59 // After this call no memory will be consumed by the object and it will be invalid. 
60 void Clear(); 
61 
62 // Will return true if a dds file has been successfully loaded. 
63 bool IsValid() const { return ((NumMipmapLayers > 0) && (NumImages > 0) && (PixelFormat != tPixelFormat::Invalid)) ? true : false; } 
64 bool IsMipmapped() const { return (NumMipmapLayers > 1) ? true : false; } 
65 bool IsCubemap() const { return IsCubeMap; } 
66 
67 // The number of mipmap levels per image is always the same if there is more than one image in the direct texture 
68 // (like for cube maps). Same for the dimensions and pixel format. 
69 int GetNumMipmapLevels() const { return NumMipmapLayers; } 
70 int GetNumImages() const { return NumImages; } 
71 int GetWidth() const { return IsValid() ? MipmapLayers[0][0]->Width : 0; } 
72 int GetHeight() const { return IsValid() ? MipmapLayers[0][0]->Height : 0; } 
73 tPixelFormat GetPixelFormat() const { return PixelFormat; } 
74 
75 // The texture is considered to have alphas if it is in a pixel format that supports them. For DXT1, the data is 
76 // checked to see if any DXT1 blocks have a binary alpha index. We could check the data for the RGBA formats, but 
77 // we don't as it shouldn't have been saved in an alpha supporting format if an all opaque texture was desired. 
78 bool IsOpaque() const
79 
80 // After calling StealTextureLayers the current object will be invalid. This call populates the passed in layer 
81 // list. If the current object is not valid the passed-in layer list is left unmodified. The layer list is appended 
82 // to. It is not emptied if there are layers on the list when passed in. This call gives management of the layers 
83 // to the caller. It does not memcpy and create new layers which is why the object becomes invalid afterwards. If 
84 // the tImageDDS is a cubemap, this function returns false and leaves the object unmodified. 
85 bool StealTextureLayers(tList<tLayer>&); 
86 
87 enum tSurfIndex 
88
89 tSurfIndex_Default
90 tSurfIndex_PosX = tSurfIndex_Default
91 tSurfIndex_NegX
92 tSurfIndex_PosY
93 tSurfIndex_NegY
94 tSurfIndex_PosZ
95 tSurfIndex_NegZ
96 tSurfIndex_NumSurfaces 
97 }; 
98 
99 // Cubemaps are always specified using a left-handed coord system even when using the OpenGL functions. 
100 enum tSurfFlag 
101
102 tSurfFlag_PosX = 1 << tSurfIndex_PosX
103 tSurfFlag_NegX = 1 << tSurfIndex_NegX
104 tSurfFlag_PosY = 1 << tSurfIndex_PosY
105 tSurfFlag_NegY = 1 << tSurfIndex_NegY
106 tSurfFlag_PosZ = 1 << tSurfIndex_PosZ
107 tSurfFlag_NegZ = 1 << tSurfIndex_NegZ
108 tSurfFlag_All = 0xFFFFFFFF 
109 }; 
110 
111 // Similar to StealTextureLayers except it steals up to 6 layer-lists if the object is a cubemap. If the tImageDDS 
112 // is not a cubemap this function returns 0 and leaves the object unmodified. If you only steal a single cubemap 
113 // side, the object becomes completely invalid afterwards. The six passed in list pointers must all be non-zero 
114 // otherwise this function does nothing. The lists are appended to. Returns the number of layer-lists that were 
115 // populated. 
116 int StealCubemapLayers(tList<tLayer> layers[tSurfIndex_NumSurfaces], uint32 sideFlags = tSurfFlag_All); 
117 
118 // You do not own the returned pointer. 
119 tLayer* GetLayer(int layerNum, int imageNum) const { return MipmapLayers[layerNum][imageNum]; } 
120 
121 // This is only for reporting the filename in case of errors. 
122 tString Filename
123 
124private
125 // This does not delete[] the ddsData. Neither does it clear the object. The caller is expected to have done that. 
126 void LoadFromMemory(const uint8* ddsData, int ddsSizeBytes, bool reverseRowOrder); 
127 bool DoDXT1BlocksHaveBinaryAlpha(tDXT1Block* blocks, int numBlocks); 
128 
129 // The surface is only valid if this is not PixelFormat_Invalid. 
130 tPixelFormat PixelFormat
131 bool IsCubeMap
132 
133 // This will be 1 for textures. For cubemaps, iNumImages == 6. 
134 int NumImages
135 
136 // If this is 1, you can consider the texture(s) to NOT be mipmapped. If there is more than a single image (like 
137 // with a cubemap), all images have the same number of mipmap layers. 
138 int NumMipmapLayers
139 const static int MaxMipmapLayers = 16
140 
141 // Cubemaps are always specified using a left-handed coord system even when using the OpenGL functions. 
142 const static int MaxImages = 6
143 tLayer* MipmapLayers[MaxMipmapLayers][MaxImages]; 
144}; 
145 
146 
147
148 
149 
150// Error objects of this type may be thrown by the tImageDDS class if something went wrong. 
151struct tDDSError : public tError 
152
153 enum class tCode 
154
155 Unknown
156 FileNonexistent
157 IncorrectExtension
158 IncorrectFileSize
159 Magic
160 IncorrectHeaderSize
161 PitchOrLinearSize
162 VolumeTexturesNotSupported
163 IncorrectPixelFormatSize
164 InconsistentPixelFormat
165 UnsupportedFourCCPixelFormat
166 UnsupportedRGBPixelFormat
167 IncorrectDXTDataSize
168 UnsupportedDXTDimensions
169 LoaderSupportsPowerOfTwoDimsOnly
170 MaxNumMipmapLevelsExceeded
171 UnsuportedFloatingPointPixelFormat
172 NumCodes 
173 }; 
174 
175 tDDSError() : tError("Unknown DDS"), Code(tCode::Unknown) { } 
176 tDDSError(tCode code, const tString& message = tString()) : tError("Direct Draw Surface: %s %s", message.ConstText(), CodeStrings[int(code)]) { } 
177 
178 static const char* CodeStrings[int(tCode::NumCodes)]; 
179 tCode Code
180}; 
181 
182