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"  |
23 | namespace tImage  |
24 | {  |
25 |   |
26 |   |
27 | struct 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.  |
34 | class tImageDDS  |
35 | {  |
36 | public:  |
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 |   |
124 | private:  |
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.  |
151 | struct tDDSError : public tError  |
152 | {  |
153 | enum class tCode  |
154 | {  |
155 | Unknown,  |
156 | FileNonexistent,  |
157 | IncorrectExtension,  |
158 | IncorrectFileSize,  |
159 | Magic,  |
160 | ,  |
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 | |