1 // dds.cpp
2 //
3 // Copyright (C) 2001, Chris Laurel
4 //
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
9 
10 #include <iostream>
11 #include <fstream>
12 #include <algorithm>
13 #include <celutil/debug.h>
14 #include <celutil/bytes.h>
15 #include <celengine/gl.h>
16 #include <celengine/glext.h>
17 #include <celengine/image.h>
18 
19 using namespace std;
20 
21 
22 
23 struct DDPixelFormat
24 {
25     uint32 size;
26     uint32 flags;
27     uint32 fourCC;
28     uint32 bpp;
29     uint32 redMask;
30     uint32 greenMask;
31     uint32 blueMask;
32     uint32 alphaMask;
33 };
34 
35 struct DDSCaps
36 {
37     uint32 caps;
38     uint32 caps2;
39     uint32 caps3;
40     uint32 caps4;
41 };
42 
43 struct DDColorKey
44 {
45     uint32 lowVal;
46     uint32 highVal;
47 };
48 
49 struct DDSurfaceDesc
50 {
51     uint32 size;
52     uint32 flags;
53     uint32 height;
54     uint32 width;
55     uint32 pitch;
56     uint32 depth;
57     uint32 mipMapLevels;
58     uint32 alphaBitDepth;
59     uint32 reserved;
60     uint32 surface;
61 
62     DDColorKey ckDestOverlay;
63     DDColorKey ckDestBlt;
64     DDColorKey ckSrcOverlay;
65     DDColorKey ckSrcBlt;
66 
67     DDPixelFormat format;
68     DDSCaps caps;
69 
70     uint32 textureStage;
71 };
72 
73 
FourCC(const char * s)74 static uint32 FourCC(const char* s)
75 {
76     return (((uint32) s[3] << 24) |
77             ((uint32) s[2] << 16) |
78             ((uint32) s[1] << 8) |
79             (uint32) s[0]);
80 }
81 
82 
83 #define DDPF_RGB    0x40
84 #define DDPF_FOURCC 0x04
85 
86 
LoadDDSImage(const string & filename)87 Image* LoadDDSImage(const string& filename)
88 {
89     ifstream in(filename.c_str(), ios::in | ios::binary);
90     if (!in.good())
91     {
92         DPRINTF(0, "Error opening DDS texture file %s.\n", filename.c_str());
93         return NULL;
94     }
95 
96     char header[4];
97     in.read(header, sizeof header);
98     if (header[0] != 'D' || header[1] != 'D' ||
99         header[2] != 'S' || header[3] != ' ')
100     {
101         DPRINTF(0, "DDS texture file %s has bad header.\n", filename.c_str());
102         return NULL;
103     }
104 
105     DDSurfaceDesc ddsd;
106     in.read(reinterpret_cast<char*>(&ddsd), sizeof ddsd);
107     LE_TO_CPU_INT32(ddsd.size, ddsd.size);
108     LE_TO_CPU_INT32(ddsd.pitch, ddsd.pitch);
109     LE_TO_CPU_INT32(ddsd.width, ddsd.width);
110     LE_TO_CPU_INT32(ddsd.height, ddsd.height);
111     LE_TO_CPU_INT32(ddsd.mipMapLevels, ddsd.mipMapLevels);
112     LE_TO_CPU_INT32(ddsd.format.flags, ddsd.format.flags);
113     LE_TO_CPU_INT32(ddsd.format.redMask, ddsd.format.redMask);
114     LE_TO_CPU_INT32(ddsd.format.greenMask, ddsd.format.greenMask);
115     LE_TO_CPU_INT32(ddsd.format.blueMask, ddsd.format.blueMask);
116     LE_TO_CPU_INT32(ddsd.format.alphaMask, ddsd.format.alphaMask);
117     LE_TO_CPU_INT32(ddsd.format.bpp, ddsd.format.bpp);
118     LE_TO_CPU_INT32(ddsd.format.fourCC, ddsd.format.fourCC);
119 
120     int format = -1;
121 
122     if (ddsd.format.fourCC != 0)
123     {
124         if (ddsd.format.fourCC == FourCC("DXT1"))
125         {
126             format = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT;
127         }
128         else if (ddsd.format.fourCC == FourCC("DXT3"))
129         {
130             format = GL_COMPRESSED_RGBA_S3TC_DXT3_EXT;
131         }
132         else if (ddsd.format.fourCC == FourCC("DXT5"))
133         {
134             format = GL_COMPRESSED_RGBA_S3TC_DXT5_EXT;
135         }
136         else
137         {
138             cerr << "Unknown FourCC in DDS file: " << ddsd.format.fourCC;
139         }
140     }
141     else
142     {
143         clog << "DDS Format: " << ddsd.format.fourCC << '\n';
144         if (ddsd.format.bpp == 32)
145         {
146             if (ddsd.format.redMask   == 0x00ff0000 &&
147                 ddsd.format.greenMask == 0x0000ff00 &&
148                 ddsd.format.blueMask  == 0x000000ff &&
149                 ddsd.format.alphaMask == 0xff000000)
150             {
151                 format = GL_BGRA_EXT;
152             }
153             else if (ddsd.format.redMask   == 0x000000ff &&
154                      ddsd.format.greenMask == 0x0000ff00 &&
155                      ddsd.format.blueMask  == 0x00ff0000 &&
156                      ddsd.format.alphaMask == 0xff000000)
157             {
158                 format = GL_RGBA;
159             }
160         }
161         else if (ddsd.format.bpp == 24)
162         {
163             if (ddsd.format.redMask   == 0x00ff0000 &&
164                 ddsd.format.greenMask == 0x0000ff00 &&
165                 ddsd.format.blueMask  == 0x000000ff)
166             {
167                 format = GL_BGR_EXT;
168             }
169             else if (ddsd.format.redMask   == 0x000000ff &&
170                      ddsd.format.greenMask == 0x0000ff00 &&
171                      ddsd.format.blueMask  == 0x00ff0000)
172             {
173                 format = GL_RGB;
174             }
175         }
176     }
177 
178     if (format == -1)
179     {
180         DPRINTF(0, "Unsupported format for DDS texture file %s.\n",
181                 filename.c_str());
182         return NULL;
183     }
184 
185     // If we have a compressed format, give up if S3 texture compression
186     // isn't supported
187     if (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT ||
188         format == GL_COMPRESSED_RGBA_S3TC_DXT3_EXT ||
189         format == GL_COMPRESSED_RGBA_S3TC_DXT5_EXT)
190     {
191         if (!ExtensionSupported("GL_EXT_texture_compression_s3tc"))
192             return NULL;
193     }
194 
195     // TODO: Verify that the reported texture size matches the amount of
196     // data expected.
197 
198     Image* img = new Image(format,
199                            (int) ddsd.width,
200                            (int) ddsd.height,
201                            max(ddsd.mipMapLevels, 1u));
202     if (img == NULL)
203         return NULL;
204 
205     in.read(reinterpret_cast<char*>(img->getPixels()), img->getSize());
206     if (!in.eof() && !in.good())
207     {
208         DPRINTF(0, "Failed reading data from DDS texture file %s.\n",
209                 filename.c_str());
210         delete img;
211         return NULL;
212     }
213 
214 #if 0
215     cout << "sizeof(ddsd) = " << sizeof(ddsd) << '\n';
216     cout << "dimensions: " << ddsd.width << 'x' << ddsd.height << '\n';
217     cout << "mipmap levels: " << ddsd.mipMapLevels << '\n';
218     cout << "fourCC: " << ddsd.format.fourCC << '\n';
219     cout << "bpp: " << ddsd.format.bpp << '\n';
220 #endif
221 
222     return img;
223 }
224