1

我使用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 上的工作方式是否可能与在 Windows 上使用 MSVC 编译器不同?

4

3 回答 3

2

对于 GCC,long 编译为 32 位时为 4 个字节,编译为 64 位时为 8 个字节。使用可以使用选项 -m32 或 -m64 来明确目标 32 或 64 位。

于 2013-06-19T17:49:26.960 回答
2

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

我开始怀疑不同编译器之间数据类型大小的差异。然后我发现了这篇文章。之后我发现 DDS 标头(使用 GCC 编译)的大小是 248,比它应该的大两倍。(MS 规范说它必须是 124 字节)。nv_dds dds 标头对其成员使用无符号长:

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 编译器将 unsigned long 视为 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 一起使用。

于 2013-05-21T08:22:08.197 回答
0

对于 ABI 相关的任务,您应该始终使用在 stdint.h 中定义的类型名称中描述其大小的类型,例如 int32_t/uint64_t,这可以在不同平台编译期间节省很多问题。(除了 big/little endian 问题)

于 2013-08-21T00:37:25.103 回答