I use Nvidia nv_dds utility to load DDS image files to use in OpenGL program. It works on Windows but fails on Linux (Ubuntu 12.10).Initially I thought the problem with nv_dds but then found that fread() reads header bytes with wrong offset on Linux (GCC 4.7)
This is the block that reads DDS file marker and then the DDS header:
// open file
FILE *fp = fopen(filename.c_str(),"rb");
if (fp == NULL) {
return false;
}
// read in file marker, make sure its a DDS file
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return false;
}
// read in DDS header
DDS_HEADER ddsh;
fread(&ddsh, 1,sizeof(DDS_HEADER) , fp);
When I look through the contents of DDS_HEADER instance I can see a couple of real values assigned to wrong properties and the rest are junk.
Then, if I comment out the "DDS" marker check fread() :
// open file
FILE *fp = fopen(filename.c_str(), "rb");
if (fp == NULL) {
return false;
}
// read in file marker, make sure its a DDS file
/* comment out for test
char filecode[4];
fread(filecode, 1, 4, fp);
if (strncmp(filecode, "DDS ", 4) != 0) {
fclose(fp);
return false;
}
*/
// read in DDS header
DDS_HEADER ddsh;
fread(&ddsh, sizeof( DDS_HEADER ),1 , fp);//sizeof( DDS_HEADER )
Then I get image width value into imageHeight property of DDS_HEADER.The rest of properties are still junk.
All this doesn't happen when I test it on Windows machine. Is it possible that fread() works differently on Linux GCC than on Windows with MSVC compiler?
I solved this and because no useful input was proposed I will answer this question by myself.
I started suspecting about differences of data type size between different compilers.Then I found this post. After that I found that the size of DDS header (compiling with GCC) is 248 which is twice larger than it should be.(MS specs say it must be 124 bytes exactly).nv_dds dds header uses unsigned long for its memebers:
typedef struct
{
unsigned long dwSize;
unsigned long dwFlags;
unsigned long dwHeight;
unsigned long dwWidth;
unsigned long dwPitchOrLinearSize;
unsigned long dwDepth;
unsigned long dwMipMapCount;
unsigned long dwReserved1[11];
DDS_PIXELFORMAT ddspf;
unsigned long dwCaps1;
unsigned long dwCaps2;
unsigned long dwReserved2[3];
}DDS_HEADER;
So it appear that MSVC compiler treats unsigned long to be of 4 bytes while GCC on Linux 8 bytes.From here is the double size of the header. I changed it all to unsigned int (also in the DDS_PIXELFORMAT header):
typedef struct
{
unsigned int dwSize;
unsigned int dwFlags;
unsigned int dwHeight;
unsigned int dwWidth;
unsigned int dwPitchOrLinearSize;
unsigned int dwDepth;
unsigned int dwMipMapCount;
unsigned int dwReserved1[11];
DDS_PIXELFORMAT ddspf;
unsigned int dwCaps1;
unsigned int dwCaps2;
unsigned int dwReserved2[3];
}DDS_HEADER;
And now it all works!Therefore it seems, contrary to what is said in some places, NVidia nv_dds is not cross platform(or/and cross compile read) and this hack should be done to get it working with GCC on Linux.