xoreos  0.0.5
dds.cpp
Go to the documentation of this file.
1 /* xoreos - A reimplementation of BioWare's Aurora engine
2  *
3  * xoreos is the legal property of its developers, whose names
4  * can be found in the AUTHORS file distributed with this source
5  * distribution.
6  *
7  * xoreos is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 3
10  * of the License, or (at your option) any later version.
11  *
12  * xoreos is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with xoreos. If not, see <http://www.gnu.org/licenses/>.
19  */
20 
25 #include "src/common/scopedptr.h"
26 #include "src/common/util.h"
27 #include "src/common/error.h"
28 #include "src/common/readstream.h"
29 
32 
33 static const uint32 kDDSID = MKTAG('D', 'D', 'S', ' ');
34 static const uint32 kDXT1ID = MKTAG('D', 'X', 'T', '1');
35 static const uint32 kDXT3ID = MKTAG('D', 'X', 'T', '3');
36 static const uint32 kDXT5ID = MKTAG('D', 'X', 'T', '5');
37 
38 static const uint32 kHeaderFlagsHasMipMaps = 0x00020000;
39 
40 static const uint32 kPixelFlagsHasAlpha = 0x00000001;
41 static const uint32 kPixelFlagsHasFourCC = 0x00000004;
42 static const uint32 kPixelFlagsIsIndexed = 0x00000020;
43 static const uint32 kPixelFlagsIsRGB = 0x00000040;
44 
45 namespace Graphics {
46 
48  load(dds);
49 }
50 
52 }
53 
55  try {
56 
57  DataType dataType;
58 
59  readHeader(dds, dataType);
60  readData (dds, dataType);
61 
62  } catch (Common::Exception &e) {
63  e.add("Failed reading DDS file");
64  throw;
65  }
66 }
67 
69  if (dds.readUint32BE() == kDDSID)
70  // We found the FourCC of a standard DDS
71  readStandardHeader(dds, dataType);
72  else
73  // FourCC not found, should be a BioWare DDS then
74  readBioWareHeader(dds, dataType);
75 }
76 
78  // All DDS header should be 124 bytes (+ 4 for the FourCC) big
79  if (dds.readUint32LE() != 124)
80  throw Common::Exception("Header size invalid");
81 
82  // DDS features
83  uint32 flags = dds.readUint32LE();
84 
85  // Image dimensions
86  uint32 height = dds.readUint32LE();
87  uint32 width = dds.readUint32LE();
88 
89  if ((width >= 0x8000) || (height >= 0x8000))
90  throw Common::Exception("Unsupported image dimensions (%ux%u)", width, height);
91 
92  dds.skip(4 + 4); // Pitch + Depth
93  //uint32 pitchOrLineSize = dds.readUint32LE();
94  //uint32 depth = dds.readUint32LE();
95  uint32 mipMapCount = dds.readUint32LE();
96 
97  // DDS doesn't provide any mip maps, only one full-size image
98  if ((flags & kHeaderFlagsHasMipMaps) == 0)
99  mipMapCount = 1;
100 
101  dds.skip(44); // Reserved
102 
103  // Read the pixel data format
104  DDSPixelFormat format;
105  format.size = dds.readUint32LE();
106  format.flags = dds.readUint32LE();
107  format.fourCC = dds.readUint32BE();
108  format.bitCount = dds.readUint32LE();
109  format.rBitMask = dds.readUint32LE();
110  format.gBitMask = dds.readUint32LE();
111  format.bBitMask = dds.readUint32LE();
112  format.aBitMask = dds.readUint32LE();
113 
114  // Detect which specific format it describes
115  detectFormat(format, dataType);
116 
117  if (!hasValidDimensions(_formatRaw, width, height))
118  throw Common::Exception("Invalid dimensions (%dx%d) for format %d", width, height, _formatRaw);
119 
120  dds.skip(16 + 4); // DDCAPS2 + Reserved
121 
122  _mipMaps.reserve(mipMapCount);
123  for (uint32 i = 0; i < mipMapCount; i++) {
124  MipMap *mipMap = new MipMap(this);
125 
126  mipMap->width = MAX<uint32>(width , 1);
127  mipMap->height = MAX<uint32>(height, 1);
128 
129  setSize(*mipMap);
130 
131  width >>= 1;
132  height >>= 1;
133 
134  _mipMaps.push_back(mipMap);
135  }
136 
137 }
138 
139 #define IsPower2(x) ((x) && (((x) & ((x) - 1)) == 0))
141  dataType = kDataTypeDirect;
142 
143  dds.seek(0);
144 
145  // Image dimensions
146  uint32 width = dds.readUint32LE();
147  uint32 height = dds.readUint32LE();
148 
149  if ((width >= 0x8000) || (height >= 0x8000))
150  throw Common::Exception("Unsupported image dimensions (%ux%u)", width, height);
151 
152  // Check that the width and height are really powers of 2
153  if (!IsPower2(width) || !IsPower2(height))
154  throw Common::Exception("Width and height must be powers of 2");
155 
156  // Always compressed
157  _compressed = true;
158 
159  // Check which compression
160  uint32 bpp = dds.readUint32LE();
161  if (bpp == 3) {
162  _hasAlpha = false;
166  } else if (bpp == 4) {
167  _hasAlpha = true;
171  } else
172  throw Common::Exception("Unsupported bytes per pixel value (%d)", bpp);
173 
174  // Sanity check for the image data size
175  uint32 dataSize = dds.readUint32LE();
176  if (((bpp == 3) && (dataSize != ((width * height) / 2))) ||
177  ((bpp == 4) && (dataSize != ((width * height) ))))
178  throw Common::Exception("Invalid data size (%dx%dx%d %d)", width, height, bpp, dataSize);
179 
180  if (!hasValidDimensions(_formatRaw, width, height))
181  throw Common::Exception("Invalid dimensions (%dx%d) for format %d", width, height, _formatRaw);
182 
183  dds.skip(4); // Some float
184 
185  // Number of bytes left for the image data
186  size_t fullDataSize = dds.size() - dds.pos();
187 
188  // Detect how many mip maps are in the DDS
189  do {
190  Common::ScopedPtr<MipMap> mipMap(new MipMap(this));
191 
192  mipMap->width = MAX<uint32>(width, 1);
193  mipMap->height = MAX<uint32>(height, 1);
194 
195  setSize(*mipMap);
196 
197  // Wouldn't fit
198  if (fullDataSize < mipMap->size)
199  break;
200 
201  fullDataSize -= mipMap->size;
202 
203  _mipMaps.push_back(mipMap.release());
204 
205  width >>= 1;
206  height >>= 1;
207 
208  } while ((width >= 1) && (height >= 1));
209 }
210 
211 void DDS::setSize(MipMap &mipMap) {
212  // Depending on the pixel format, set the image data size in bytes
213 
214  mipMap.size = getDataSize(_formatRaw, mipMap.width, mipMap.height);
215 }
216 
218  for (MipMaps::iterator mipMap = _mipMaps.begin(); mipMap != _mipMaps.end(); ++mipMap) {
219  (*mipMap)->data.reset(new byte[(*mipMap)->size]);
220 
221  if (dataType == kDataType4444) {
222 
223  byte *data = (*mipMap)->data.get();
224  for (uint32 i = 0; i < (uint32)((*mipMap)->width * (*mipMap)->height); i++, data += 4) {
225  const uint16 pixel = dds.readUint16LE();
226 
227  data[0] = ( pixel & 0x0000000F ) << 4;
228  data[1] = ((pixel & 0x000000F0) >> 4) << 4;
229  data[2] = ((pixel & 0x00000F00) >> 8) << 4;
230  data[3] = ((pixel & 0x0000F000) >> 12) << 4;
231  }
232 
233  } else if (dataType == kDataTypeDirect)
234  if (dds.read((*mipMap)->data.get(), (*mipMap)->size) != (*mipMap)->size)
236 
237  }
238 }
239 
240 void DDS::detectFormat(const DDSPixelFormat &format, DataType &dataType) {
241  // Big, ugly big pixel format description => format mapping
242 
243  dataType = kDataTypeDirect;
244 
245  if ((format.flags & kPixelFlagsHasFourCC) && (format.fourCC == kDXT1ID)) {
246  _compressed = true;
247  _hasAlpha = false;
251  } else if ((format.flags & kPixelFlagsHasFourCC) && (format.fourCC == kDXT3ID)) {
252  _compressed = true;
253  _hasAlpha = true;
257  } else if ((format.flags & kPixelFlagsHasFourCC) && (format.fourCC == kDXT5ID)) {
258  _compressed = true;
259  _hasAlpha = true;
263  } else if ((format.flags & kPixelFlagsIsRGB) && (format.flags & kPixelFlagsHasAlpha) &&
264  (format.bitCount == 32) &&
265  (format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
266  (format.bBitMask == 0x000000FF) && (format.aBitMask == 0xFF000000)) {
267  _compressed = false;
268  _hasAlpha = true;
272  } else if ((format.flags & kPixelFlagsIsRGB) && !(format.flags & kPixelFlagsHasAlpha) &&
273  (format.bitCount == 24) &&
274  (format.rBitMask == 0x00FF0000) && (format.gBitMask == 0x0000FF00) &&
275  (format.bBitMask == 0x000000FF)) {
276  _compressed = false;
277  _hasAlpha = false;
281 
282  } else if ((format.flags & kPixelFlagsIsRGB) && (format.flags & kPixelFlagsHasAlpha) &&
283  (format.bitCount == 16) &&
284  (format.rBitMask == 0x00007C00) && (format.gBitMask == 0x000003E0) &&
285  (format.bBitMask == 0x0000001F) && (format.aBitMask == 0x00008000)) {
286  _compressed = false;
287  _hasAlpha = true;
291 
292  warning("Found untested DDS RGB5A1 data");
293 
294  } else if ((format.flags & kPixelFlagsIsRGB) && !(format.flags & kPixelFlagsHasAlpha) &&
295  (format.bitCount == 16) &&
296  (format.rBitMask == 0x0000F800) && (format.gBitMask == 0x000007E0) &&
297  (format.bBitMask == 0x0000001F)) {
298  _compressed = false;
299  _hasAlpha = false;
303 
304  warning("Found untested DDS RGB5 data");
305 
306  } else if ((format.flags & kPixelFlagsIsRGB) && (format.flags & kPixelFlagsHasAlpha) &&
307  (format.bitCount == 16) &&
308  (format.rBitMask == 0x00000F00) && (format.gBitMask == 0x000000F0) &&
309  (format.bBitMask == 0x0000000F) && (format.aBitMask == 0x0000F000)) {
310  _compressed = false;
311  _hasAlpha = true;
315 
316  dataType = kDataType4444;
317 
318  } else if (format.flags & kPixelFlagsIsIndexed)
319  // Hopefully, we'll never need to support that :P
320  throw Common::Exception("Unsupported feature: Palette");
321  else
322  // We'll see if there's more formats in the data files :P
323  throw Common::Exception("Unknown pixel format (%X, %X, %d, %X, %X, %X, %X)",
324  format.flags, format.fourCC, format.bitCount,
325  format.rBitMask, format.gBitMask, format.bBitMask, format.aBitMask);
326 }
327 
328 } // End of namespace Graphics
#define MKTAG(a0, a1, a2, a3)
A wrapper macro used around four character constants, like &#39;DATA&#39;, to ensure portability.
Definition: endianness.h:140
uint16 readUint16LE()
Read an unsigned 16-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:122
void add(const char *s,...) GCC_PRINTF(2
Definition: error.cpp:58
DDS texture (DirectDraw Surface or BioWare&#39;s own format) loading).
uint32 aBitMask
Bit mask for the alpha component.
Definition: dds.h:64
uint32 readUint32LE()
Read an unsigned 32-bit word stored in little endian (LSB first) order from the stream and return it...
Definition: readstream.h:133
uint32 size
The size of the image data in bytes.
Definition: dds.h:57
virtual size_t seek(ptrdiff_t offset, Origin whence=kOriginBegin)=0
Sets the stream position indicator for the stream.
uint32 bitCount
Number of bits per pixel.
Definition: dds.h:60
uint32 bBitMask
Bit mask for the blue color component.
Definition: dds.h:63
PointerType release()
Returns the plain pointer value and releases ScopedPtr.
Definition: scopedptr.h:103
static bool hasValidDimensions(PixelFormatRaw format, int32 width, int32 height)
Are these image dimensions valid for this format?
Definition: util.h:73
static const uint32 kPixelFlagsIsIndexed
Definition: dds.cpp:42
void readStandardHeader(Common::SeekableReadStream &dds, DataType &dataType)
Definition: dds.cpp:77
static const uint32 kDXT3ID
Definition: dds.cpp:35
static const uint32 kPixelFlagsIsRGB
Definition: dds.cpp:43
PixelDataType _dataType
Definition: decoder.h:124
static const uint32 kPixelFlagsHasFourCC
Definition: dds.cpp:41
void readData(Common::SeekableReadStream &dds, DataType dataType)
Definition: dds.cpp:217
int height
The mip map&#39;s height.
Definition: decoder.h:53
Exception that provides a stack of explanations.
Definition: error.h:36
A simple scoped smart pointer template.
Basic exceptions to throw.
The specific pixel format of the included image data.
Definition: dds.h:56
uint16_t uint16
Definition: types.h:202
PixelFormat _format
Definition: decoder.h:122
Utility templates and functions.
uint32 fourCC
The FourCC to detect the format by.
Definition: dds.h:59
virtual size_t skip(ptrdiff_t offset)
Skip the specified number of bytes, adding that offset to the current position in the stream...
Definition: readstream.h:317
static const uint32 kDXT1ID
Definition: dds.cpp:34
PixelFormatRaw _formatRaw
Definition: decoder.h:123
virtual size_t read(void *dataPtr, size_t dataSize)=0
Read data from the stream.
static const uint32 kPixelFlagsHasAlpha
Definition: dds.cpp:40
Image related utility functions.
StackException Exception
Definition: error.h:59
const Exception kReadError("Read error")
Exception when reading from a stream failed.
Definition: error.h:62
static const uint32 kDDSID
Definition: dds.cpp:33
A scoped plain pointer, allowing pointer-y access and normal deletion.
Definition: scopedptr.h:120
void readHeader(Common::SeekableReadStream &dds, DataType &dataType)
Definition: dds.cpp:68
void warning(const char *s,...)
Definition: util.cpp:33
virtual size_t size() const =0
Obtains the total size of the stream, measured in bytes.
uint32 flags
Features of the image data.
Definition: dds.h:58
static uint32 getDataSize(PixelFormatRaw format, int32 width, int32 height)
Return the number of bytes necessary to hold an image of these dimensions and in this format...
Definition: util.h:43
Basic reading stream interfaces.
virtual size_t pos() const =0
Obtains the current value of the stream position indicator of the stream.
uint32 readUint32BE()
Read an unsigned 32-bit word stored in big endian (MSB first) order from the stream and return it...
Definition: readstream.h:166
uint32 gBitMask
Bit mask for the green color component.
Definition: dds.h:62
void setSize(MipMap &mipMap)
Definition: dds.cpp:211
int width
The mip map&#39;s width.
Definition: decoder.h:52
void readBioWareHeader(Common::SeekableReadStream &dds, DataType &dataType)
Definition: dds.cpp:140
uint32_t uint32
Definition: types.h:204
uint32 rBitMask
Bit mask for the red color component.
Definition: dds.h:61
static const uint32 kDXT5ID
Definition: dds.cpp:36
DDS(Common::SeekableReadStream &dds)
Definition: dds.cpp:47
static const uint32 kHeaderFlagsHasMipMaps
Definition: dds.cpp:38
#define IsPower2(x)
Definition: dds.cpp:139
uint32 size
The mip map&#39;s size in bytes.
Definition: decoder.h:54
Interface for a seekable & readable data stream.
Definition: readstream.h:265
void detectFormat(const DDSPixelFormat &format, DataType &dataType)
Definition: dds.cpp:240
uint8 byte
Definition: types.h:209
void load(Common::SeekableReadStream &dds)
Definition: dds.cpp:54