FS2_Open
Open source remastering of the Freespace 2 engine
ddsutils.cpp
Go to the documentation of this file.
1 
2 #include "ddsutils/ddsutils.h"
3 #include "cfile/cfile.h"
4 #include "osapi/osregistry.h"
5 
6 
7 /* Currently supported formats:
8  * DXT1a (compressed)
9  * DXT1c (compressed)
10  * DXT3 (compressed)
11  * DXT5 (compressed)
12  * uncompressed 1555 (16-bit, 1-bit being alpha)
13  * uncompressed 8888 (32-bit)
14  * uncompressed 888 (24-bit, no alpha)
15  * paletted 8-bit (256 colors)
16  */
17 
18 
21 
22 // power of two check, to verify that a given DDS will always be usable since
23 // some may not be power-of-2
24 static inline int is_power_of_two(int w, int h)
25 {
26  return ( (w && !(w & (w-1))) && (h && !(h & (h-1))) );
27 }
28 
29 
30 int dds_read_header(const char *filename, CFILE *img_cfp, int *width, int *height, int *bpp, int *compression_type, int *levels, int *size, ubyte *palette)
31 {
32  DDSURFACEDESC2 dds_header;
33  int code = 0;
34  CFILE *ddsfile;
35  char real_name[MAX_FILENAME_LEN];
36  int retval = DDS_ERROR_NONE;
37  int ct = DDS_UNCOMPRESSED;
38  int bits = 0;
39  int i, is_cubemap = 0;
40  int d_width = 0, d_height = 0, d_depth = 0, d_size = 0;
41 
42 
43  if (img_cfp == NULL) {
44  // this better not happen.. ever
45  Assert(filename != NULL);
46 
47  // make sure there is an extension
48  strcpy_s(real_name, filename);
49  char *p = strchr(real_name, '.');
50  if (p) { *p=0; }
51  strcat_s(real_name, ".dds");
52 
53  // try to open the file
54  ddsfile = cfopen(real_name, "rb");
55 
56  // file not found
57  if (ddsfile == NULL)
59  } else {
60  ddsfile = img_cfp;
61  }
62 
63  // read the code
64  code = cfread_int(ddsfile);
65 
66  // check it
67  if (code != DDS_FILECODE) {
68  retval = DDS_ERROR_BAD_HEADER;
69  goto Done;
70  }
71 
72  // read header variables
73  dds_header.dwSize = cfread_uint(ddsfile);
74  dds_header.dwFlags = cfread_uint(ddsfile);
75  dds_header.dwHeight = cfread_uint(ddsfile);
76  dds_header.dwWidth = cfread_uint(ddsfile);
77  dds_header.dwPitchOrLinearSize = cfread_uint(ddsfile);
78  dds_header.dwDepth = cfread_uint(ddsfile);
79  dds_header.dwMipMapCount = cfread_uint(ddsfile);
80 
81  // skip over the crap we don't care about
82  for (i = 0; i < 11; i++)
83  cfread_uint(ddsfile);
84 
85  dds_header.ddpfPixelFormat.dwSize = cfread_uint(ddsfile);
86  dds_header.ddpfPixelFormat.dwFlags = cfread_uint(ddsfile);
87  dds_header.ddpfPixelFormat.dwFourCC = cfread_uint(ddsfile);
88  dds_header.ddpfPixelFormat.dwRGBBitCount = cfread_uint(ddsfile);
89  dds_header.ddpfPixelFormat.dwRBitMask = cfread_uint(ddsfile);
90  dds_header.ddpfPixelFormat.dwGBitMask = cfread_uint(ddsfile);
91  dds_header.ddpfPixelFormat.dwBBitMask = cfread_uint(ddsfile);
92  dds_header.ddpfPixelFormat.dwRGBAlphaBitMask = cfread_uint(ddsfile);
93 
94  dds_header.ddsCaps.dwCaps1 = cfread_uint(ddsfile);
95  dds_header.ddsCaps.dwCaps2 = cfread_uint(ddsfile);
96 
97  // sanity
98  if (dds_header.dwDepth == 0)
99  dds_header.dwDepth = 1;
100 
101  if (dds_header.dwMipMapCount < 1)
102  dds_header.dwMipMapCount = 1;
103 
104  d_width = dds_header.dwWidth;
105  d_height = dds_header.dwHeight;
106  d_depth = dds_header.dwDepth;
107 
108  // calculate the type and size of the data
109  if (dds_header.ddpfPixelFormat.dwFlags & DDPF_FOURCC) {
110  // did I mention lately that I hate Microsoft?
111  // this is here to fix the situation where Microsoft doesn't follow their own docs
112  if ( dds_header.dwPitchOrLinearSize <= 0 ) {
113  // calculate the block size, mult by 8 for DXT1, 16 for DXT3 & 5
114  d_size = ((dds_header.dwWidth + 3)/4) * ((dds_header.dwHeight + 3)/4) * ((dds_header.dwDepth + 3)/4) * ((dds_header.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 8 : 16);
115  } else {
116  d_size = dds_header.dwPitchOrLinearSize;
117  }
118 
119  // if we have mipmaps then compute them into the final size too
120  for (i = 1; i < (int)dds_header.dwMipMapCount; i++) {
121  // reduce size by half for the next pass
122  d_width /= 2;
123  d_height /= 2;
124 
125  if (d_width <= 0)
126  d_width = 1;
127 
128  if (d_height <= 0)
129  d_height = 1;
130 
131  // size of data block (4x4)
132  d_size += ((d_width + 3) / 4) * ((d_height + 3) / 4) * ((dds_header.ddpfPixelFormat.dwFourCC == FOURCC_DXT1) ? 8 : 16);
133  }
134 
135  if ( dds_header.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP ) {
136  if ( !(dds_header.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP_ALLFACES) ) {
137  retval = DDS_ERROR_CUBEMAP_FACES;
138  ct = DDS_DXT_INVALID;
139  goto Done;
140  }
141 
142  // it's a valid cubemap, probably
143  is_cubemap = 1;
144 
145  // the previously computed size is just per face, mult by 6 for full size
146  d_size *= 6;
147  }
148 
149  Assert( d_size > 0 );
150  *size = d_size;
151 
152  switch (dds_header.ddpfPixelFormat.dwFourCC) {
153  case FOURCC_DXT1:
154  bits = 24;
155  ct = (is_cubemap) ? DDS_CUBEMAP_DXT1 : DDS_DXT1;
156  break;
157 
158  case FOURCC_DXT3:
159  bits = 32;
160  ct = (is_cubemap) ? DDS_CUBEMAP_DXT3 : DDS_DXT3;
161  break;
162 
163  case FOURCC_DXT5:
164  bits = 32;
165  ct = (is_cubemap) ? DDS_CUBEMAP_DXT5 : DDS_DXT5;
166  break;
167 
168  // dxt2 and dxt4 aren't supported
169  case FOURCC_DXT2:
170  case FOURCC_DXT4:
171  retval = DDS_ERROR_UNSUPPORTED;
172  ct = DDS_DXT_INVALID;
173  goto Done;
174  break;
175 
176  // none of the above
177  default:
178  retval = DDS_ERROR_INVALID_FORMAT;
179  ct = DDS_DXT_INVALID;
180  goto Done;
181  break;
182  }
183  } else if ( dds_header.ddpfPixelFormat.dwFlags & (DDPF_RGB | DDPF_PALETTEINDEXED8) ) {
184  d_size += d_width * d_height * d_depth * (dds_header.ddpfPixelFormat.dwRGBBitCount / 8);
185 
186  // calculate full data size for all mipmap levels
187  for (i = 1; i < (int)dds_header.dwMipMapCount; i++) {
188  d_width /= 2;
189  d_height /= 2;
190  d_depth /= 2;
191 
192  if (d_width < 1)
193  d_width = 1;
194 
195  if (d_height < 1)
196  d_height = 1;
197 
198  if (d_depth < 1)
199  d_depth = 1;
200 
201  d_size += d_width * d_height * d_depth * (dds_header.ddpfPixelFormat.dwRGBBitCount / 8);
202  }
203 
204  Assert( d_size > 0 );
205 
206  if ( dds_header.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP ) {
207  if ( !(dds_header.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP_ALLFACES) ) {
208  retval = DDS_ERROR_CUBEMAP_FACES;
209  ct = DDS_DXT_INVALID;
210  goto Done;
211  }
212 
213  // it's a valid cubemap, probably
214  is_cubemap = 1;
215 
216  // the previously computed size is just per face, mult by 6 for full size
217  d_size *= 6;
218  }
219 
220  *size = d_size;
221 
222  bits = dds_header.ddpfPixelFormat.dwRGBBitCount;
223  ct = (is_cubemap) ? DDS_CUBEMAP_UNCOMPRESSED : DDS_UNCOMPRESSED;
224  } else {
225  // it's not a readable format
226  retval = DDS_ERROR_INVALID_FORMAT;
227  goto Done;
228  }
229 
230  // we don't support compressed, non-power-of-2 images
231  if ( (ct != DDS_UNCOMPRESSED) && (!is_power_of_two(dds_header.dwWidth, dds_header.dwHeight)) ) {
232  retval = DDS_ERROR_NON_POWER_OF_2;
233  goto Done;
234  }
235 
236  // make sure that the video card can handle compressed textures before using them
237  if ( !Use_compressed_textures && (ct != DDS_UNCOMPRESSED) && (ct != DDS_DXT_INVALID) ) {
238  retval = DDS_ERROR_NO_COMPRESSION;
239  goto Done;
240  }
241 
242  // stuff important info
243  if (bpp)
244  *bpp = bits;
245 
246  if (compression_type)
247  *compression_type = ct;
248 
249  if (width)
250  *width = dds_header.dwWidth;
251 
252  if (height)
253  *height = dds_header.dwHeight;
254 
255  if (levels)
256  *levels = dds_header.dwMipMapCount;
257 
258  if (palette && (bits == 8)) {
259  cfseek(ddsfile, DDS_OFFSET, CF_SEEK_SET);
260  cfread(palette, 1, 1024, ddsfile);
261  }
262 
263 
264 Done:
265  if (img_cfp == NULL) {
266  // close file and return
267  cfclose(ddsfile);
268  ddsfile = NULL;
269  }
270 
271  return retval;
272 }
273 
274 //reads pixel info from a dds file
275 int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
276 {
277  int retval;
278  int w,h,ct,lvl;
279  int size = 0, bits = 0;
280  CFILE *cfp;
281  char real_name[MAX_FILENAME_LEN];
282 
283  // this better not happen.. ever
284  Assert(filename != NULL);
285 
286  // make sure there is an extension
287  strcpy_s(real_name, filename);
288  char *p = strchr(real_name, '.');
289  if (p) { *p = 0; }
290  strcat_s(real_name, ".dds");
291 
292  // open it up and go to the data section
293  cfp = cfopen(real_name, "rb", CFILE_NORMAL, cf_type);
294 
295  // just in case
296  if (cfp == NULL)
298 
299  // read the header -- if its at this stage, it should be legal.
300  retval = dds_read_header(real_name, cfp, &w, &h, &bits, &ct, &lvl, &size);
301  Assert(retval == DDS_ERROR_NONE);
302 
303  // this really shouldn't be needed but better safe than sorry
304  if (retval != DDS_ERROR_NONE) {
305  return retval;
306  }
307 
309 
310  // read in the data
311  cfread(data, 1, size, cfp);
312 
313  if (bpp)
314  *bpp = (ubyte)bits;
315 
316  // we look done here
317  cfclose(cfp);
318 
319  return DDS_ERROR_NONE;
320 }
321 
322 // save some image data as a DDS image
323 // NOTE: we only support, uncompressed, 24-bit RGB and 32-bit RGBA images here!!
324 void dds_save_image(int width, int height, int bpp, int num_mipmaps, ubyte *data, int cubemap, const char *filename)
325 {
326  DDSURFACEDESC2 dds_header;
327  char real_filename[MAX_FILENAME_LEN];
328 
329  if (data == NULL) {
330  Int3();
331  return;
332  }
333 
334  // header size check
335  if (sizeof(DDSURFACEDESC2) != 124) {
336  Int3();
337  return;
338  }
339 
340 
341  // not that that's out of the way we can get down to business, constructing a sane filename
342  memset( &real_filename, 0, sizeof(real_filename) );
343 
344  int count = os_config_read_uint(NULL, "ImageExportNum", 0);
345 
346  if (count > 999)
347  count = 0;
348 
349  if (filename == NULL) {
350  if (cubemap) {
351  sprintf(real_filename, "cubemap%.3d.dds", count++);
352  } else {
353  sprintf(real_filename, "image%.3d.dds", count++);
354  }
355  os_config_write_uint(NULL, "ImageExportNum", count);
356  } else {
357  Assert( strlen(filename) < MAX_FILENAME_LEN-5 );
358 
359  strcpy_s(real_filename, filename);
360 
361  char *p = strchr(real_filename, '.');
362  if (p) { *p = 0; }
363 
364  strcat_s(real_filename, ".dds");
365  }
366 
367  CFILE *image = cfopen( real_filename, "wb", CFILE_NORMAL, CF_TYPE_CACHE );
368 
369  if (image == NULL) {
370  mprintf(("Unable to open DDS image for saving!!\n"));
371  return;
372  }
373 
374  if (num_mipmaps < 1)
375  num_mipmaps = 1;
376 
377  // we have a filename and file handle, so now lets create our DDS header...
378  memset( &dds_header, 0, sizeof(DDSURFACEDESC2) );
379 
381  uint pixel_flags = DDPF_RGB;
382  uint caps1 = DDSCAPS_TEXTURE;
383  uint caps2 = 0;
384 
385  if (bpp == 32) {
386  pixel_flags |= DDPF_ALPHAPIXELS;
387  }
388 
389  if (num_mipmaps > 1) {
390  flags |= DDSD_MIPMAPCOUNT;
391  caps1 |= (DDSCAPS_COMPLEX | DDSCAPS_MIPMAP);
392  }
393 
394  if (cubemap) {
395  caps1 |= DDSCAPS_COMPLEX;
397  }
398 
399  dds_header.dwSize = 124;
400  dds_header.dwFlags = flags;
401  dds_header.dwHeight = height;
402  dds_header.dwWidth = width;
403  dds_header.dwPitchOrLinearSize = (width * (bpp >> 3));
404  dds_header.dwDepth = 0;
405  dds_header.dwMipMapCount = num_mipmaps;
406 
407  dds_header.ddpfPixelFormat.dwSize = 32;
408  dds_header.ddpfPixelFormat.dwFlags = pixel_flags;
409  dds_header.ddpfPixelFormat.dwFourCC = 0;
410  dds_header.ddpfPixelFormat.dwRGBBitCount = bpp;
411  dds_header.ddpfPixelFormat.dwRBitMask = 0x00ff0000;
412  dds_header.ddpfPixelFormat.dwGBitMask = 0x0000ff00;
413  dds_header.ddpfPixelFormat.dwBBitMask = 0x000000ff;
414  dds_header.ddpfPixelFormat.dwRGBAlphaBitMask = (bpp == 32) ? 0xff000000 : 0x00000000;
415 
416  dds_header.ddsCaps.dwCaps1 = caps1;
417  dds_header.ddsCaps.dwCaps2 = caps2;
418 
419  // now save it all...
420  uint dds_id = DDS_FILECODE;
421  cfwrite(&dds_id, 1, 4, image);
422  cfwrite(&dds_header, 1, sizeof(DDSURFACEDESC2), image);
423 
424  int faces = (cubemap) ? 6 : 1;
425  int f_width = width;
426  int f_height = height;
427  int f_size = 0, f_offset = 0;
428 
429  // cubemaps are written:
430  // face 1
431  // all face 1 mipmaps
432  // face 2
433  // all face 2 mipmaps
434  // ... etc.
435 
436  for (int i = 0; i < faces; i++) {
437  for (int j = 0; j < num_mipmaps; j++) {
438  f_size = (f_width * f_height * (bpp >> 3));
439 
440  cfwrite(data + f_offset, 1, f_size, image);
441 
442  // increase offset for next mipmap level
443  f_offset += f_size;
444 
445  // half width and height for next mipmap level
446  f_width /= 2;
447  f_height /= 2;
448 
449  if (f_width < 1)
450  f_width = 1;
451 
452  if (f_height < 1)
453  f_height = 1;
454  }
455 
456  // reset width and height to original values for the next face
457  f_width = width;
458  f_height = height;
459  }
460 
461  // done! now lets go get something to eat...
462  cfclose(image);
463 }
464 
465 
466 // returns string representation of DDS_** error code
467 const char *dds_error_string(int code)
468 {
469  switch (code)
470  {
471  case DDS_ERROR_NONE:
472  return "No error";
473 
475  return "File not found";
476 
478  return "Filecode did not equal \"DDS \"";
479 
481  return "DDS was in an unsupported/unknown format";
482 
484  return "DDS format was known but is not supported (ie. DXT2/DXT4)";
485 
487  return "DDS is compressed but compression support is not enabled";
488 
490  return "Cannot load DDS if not power-of-2";
491 
493  return "Cubemaps must have all 6 faces";
494 
495  default:
496  return "Abort, retry, fail?";
497  }
498 }
#define MAX_FILENAME_LEN
Definition: pstypes.h:324
#define CFILE_NORMAL
Definition: cfile.h:89
int i
Definition: multi_pxo.cpp:466
uint os_config_read_uint(const char *section, const char *name, uint default_value)
Definition: osregistry.cpp:372
#define DDS_ERROR_INVALID_FORMAT
Definition: ddsutils.h:14
#define DDS_ERROR_BAD_HEADER
Definition: ddsutils.h:15
GLfloat GLfloat GLfloat GLfloat h
Definition: Glext.h:7280
#define FOURCC_DXT5
Definition: ddsutils.h:42
int dds_read_header(const char *filename, CFILE *img_cfp, int *width, int *height, int *bpp, int *compression_type, int *levels, int *size, ubyte *palette)
Definition: ddsutils.cpp:30
int cfread(void *buf, int elsize, int nelem, CFILE *fp)
uint dwRGBAlphaBitMask
Definition: ddsutils.h:121
#define DDSD_PITCH
Definition: ddsutils.h:48
Assert(pm!=NULL)
#define DDS_ERROR_NON_POWER_OF_2
Definition: ddsutils.h:17
#define FOURCC_DXT2
Definition: ddsutils.h:39
#define DDSD_PIXELFORMAT
Definition: ddsutils.h:55
#define mprintf(args)
Definition: pstypes.h:238
#define DDS_FILECODE
Definition: ddsutils.h:44
GLenum GLsizei GLenum GLenum const GLvoid * image
Definition: Glext.h:5230
#define DDS_CUBEMAP_DXT3
Definition: ddsutils.h:28
#define FOURCC_DXT4
Definition: ddsutils.h:41
Definition: cfile.h:28
#define DDSCAPS_TEXTURE
Definition: ddsutils.h:64
#define CF_SEEK_SET
Definition: cfile.h:24
#define DDSCAPS2_CUBEMAP_ALLFACES
Definition: ddsutils.h:75
GLsizeiptr size
Definition: Glext.h:5496
#define Int3()
Definition: pstypes.h:292
#define DDS_ERROR_NO_COMPRESSION
Definition: ddsutils.h:16
#define DDSD_CAPS
Definition: ddsutils.h:58
void os_config_write_uint(const char *section, const char *name, uint value)
Definition: osregistry.cpp:269
#define DDPF_ALPHAPIXELS
Definition: ddsutils.h:49
GLint GLsizei width
Definition: Gl.h:1505
#define DDSCAPS_MIPMAP
Definition: ddsutils.h:63
HWND DWORD code
Definition: vddraw.h:425
typedef int(SCP_EXT_CALLCONV *SCPDLL_PFVERSION)(SCPDLL_Version *)
#define DDSCAPS_COMPLEX
Definition: ddsutils.h:61
#define DDPF_FOURCC
Definition: ddsutils.h:50
int Texture_compression_available
Definition: ddsutils.cpp:19
unsigned int uint
Definition: pstypes.h:64
#define cfopen(...)
Definition: cfile.h:134
#define DDS_ERROR_CUBEMAP_FACES
Definition: ddsutils.h:18
GLenum GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * bits
Definition: Glext.h:10400
#define DDS_DXT5
Definition: ddsutils.h:25
char * filename
int Use_compressed_textures
Definition: ddsutils.cpp:20
#define w(p)
Definition: modelsinc.h:68
sprintf(buf,"(%f,%f,%f)", v3->xyz.x, v3->xyz.y, v3->xyz.z)
#define DDSCAPS2_CUBEMAP
Definition: ddsutils.h:66
int cfwrite(const void *buf, int elsize, int nelem, CFILE *cfile)
Definition: cfile.cpp:1414
cfp
Definition: cfile.cpp:1061
#define DDS_CUBEMAP_UNCOMPRESSED
Definition: ddsutils.h:26
uint dwMipMapCount
Definition: ddsutils.h:110
unsigned char ubyte
Definition: pstypes.h:62
uint dwRGBBitCount
Definition: ddsutils.h:117
#define DDS_UNCOMPRESSED
Definition: ddsutils.h:22
#define DDSD_MIPMAPCOUNT
Definition: ddsutils.h:59
GLbitfield flags
Definition: Glext.h:6722
#define CF_TYPE_CACHE
Definition: cfile.h:72
int dds_read_bitmap(const char *filename, ubyte *data, ubyte *bpp, int cf_type)
Definition: ddsutils.cpp:275
#define DDS_OFFSET
Definition: ddsutils.h:136
#define DDS_DXT3
Definition: ddsutils.h:24
GLint GLsizei GLsizei height
Definition: Gl.h:1505
#define strcat_s(...)
Definition: safe_strings.h:68
struct _DDSURFACEDESC2::@14 ddsCaps
GLubyte GLubyte GLubyte GLubyte w
Definition: Glext.h:5679
#define DDS_ERROR_NONE
Definition: ddsutils.h:11
const char * dds_error_string(int code)
Definition: ddsutils.cpp:467
#define FOURCC_DXT1
Definition: ddsutils.h:38
GLfloat GLfloat p
Definition: Glext.h:8373
struct _DDSURFACEDESC2::@13 ddpfPixelFormat
#define DDS_ERROR_INVALID_FILENAME
Definition: ddsutils.h:12
GLenum GLsizei GLenum GLenum const GLvoid * data
Definition: Gl.h:1509
uint dwPitchOrLinearSize
Definition: ddsutils.h:108
#define DDPF_RGB
Definition: ddsutils.h:54
int cfread_int(CFILE *file, int ver, int deflt)
Definition: cfile.cpp:1164
GLint GLsizei count
Definition: Gl.h:1491
#define DDS_CUBEMAP_DXT1
Definition: ddsutils.h:27
#define DDS_CUBEMAP_DXT5
Definition: ddsutils.h:29
#define DDS_ERROR_UNSUPPORTED
Definition: ddsutils.h:13
int cfclose(CFILE *cfile)
Definition: cfile.cpp:895
#define DDSD_WIDTH
Definition: ddsutils.h:56
#define DDS_DXT1
Definition: ddsutils.h:23
#define DDS_DXT_INVALID
Definition: ddsutils.h:21
void dds_save_image(int width, int height, int bpp, int num_mipmaps, ubyte *data, int cubemap, const char *filename)
Definition: ddsutils.cpp:324
#define FOURCC_DXT3
Definition: ddsutils.h:40
#define DDSD_HEIGHT
Definition: ddsutils.h:57
uint cfread_uint(CFILE *file, int ver, uint deflt)
Definition: cfile.cpp:1178
#define DDPF_PALETTEINDEXED8
Definition: ddsutils.h:53
#define strcpy_s(...)
Definition: safe_strings.h:67
int cfseek(CFILE *fp, int offset, int where)