无法在Linux上读取DDS图像头

时间:2013-05-20 07:59:23

标签: c++ opengl gcc nvidia dds-format

我使用Nvidia nv_dds实用程序加载DDS图像文件以在OpenGL程序中使用。它适用于Windows但在Linux上失败(Ubuntu 12.10)。最初我认为nv_dds存在问题,但后来发现fread()在Linux上读取错误偏移的头字节(GCC 4.7)

这是读取DDS文件标记然后读取DDS标题的块:

// 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);

当我查看DDS_HEADER实例的内容时,我可以看到分配给错误属性的几个实际值,其余的都是垃圾。

然后,如果我注释掉“DDS”标记,请检查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 )

然后我将图像宽度值变为DDS_HEADER的imageHeight属性。其余属性仍然是垃圾。

当我在Windows机器上测试时,所有这一切都不会发生。 fread()在Linux GCC上的工作方式是否可能与使用MSVC编译器的Windows不同?

3 个答案:

答案 0 :(得分:2)

GCC long在编译为32位时为4个字节,在64位时为8个字节。使用可以使用选项-m32或-m64显式目标32或64位。

答案 1 :(得分:1)

我解决了这个问题,因为没有提出有用的意见,我将自己回答这个问题。

我开始怀疑不同编译器之间数据类型大小的差异。然后我发现了这个post。之后我发现DDS头的大小(用GCC编译)是248,比它应该大两倍。(MS规格说它必须是124字节).nv_dds dds头为其成员使用unsigned long:

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;

因此,似乎MSVC编译器将无符号长整数视为4字节,而Linux上的GCC则为8字节。这里是标题的双倍大小。我把它全部更改为unsigned int(也在DDS_PIXELFORMAT标头中):

 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;

现在一切正常!因此看起来,与某些地方所说的相反,NVidia nv_dds不是跨平台(或/和交叉编译读取),应该完成这个hack以使其在Linux上使用GCC 。

答案 2 :(得分:0)

对于与ABI相关的任务,你应该总是使用在stdint.h中定义的类型名称描述的类型,如int32_t / uint64_t,这可以在不同平台的编译过程中节省很多问题。小端问题)