3

我正在尝试将 bmp 文件读入我的程序,但遇到了一些问题。读入文件后,如果我告诉它打印 pBmp->header.fileSize 它说 16 但如果我在十六进制编辑器中查看它,如果我将值修改为正确的文件大小,则文件大小部分有 F6 7A 10 00在十六进制中,它会说 F6 7A F6 7A 10 00 但这会进入 resv1,它应该始终为零。我知道这只是读取 1 个像素的数据,我遇到的另一个问题是,当我尝试使用 while 循环读取像素直到文件末尾出现分段错误时。我确实花了几个小时在谷歌上搜索试图解决这个问题,但我运气不佳。

// The BMPHEADER structure.
typedef struct {
    byte        sigB;
    byte        sigM;
    int32_t     fileSize;
    int16_t     resv1;
    int16_t     resv2;
    int32_t     pixelOffset;
} tBmpHeader;

// The BMPINFOHEADER structure.
typedef struct {
    int32_t     size;
    int32_t     width;
    int32_t     height;
    int16_t     colorPlanes;
    int16_t     bitsPerPixel;
    byte        zeros[24];
} tBmpInfoHeader;

typedef uint8_t byte;

typedef struct {
    byte blue;
    byte green;
    byte red;
} tPixel;

// A BMP image consists of the BMPHEADER and BMPINFOHEADER structures, and the 2D pixel array.
typedef struct {
    tBmpHeader      header;
    tBmpInfoHeader  infoHeader;
    tPixel          **pixel;
} tBmp;

tPixel **BmpPixelAlloc(int pWidth, int pHeight)
{
    tPixel **pixels = (tPixel **)malloc (pHeight * sizeof(tPixel *));
    for (int row = 0; row < pHeight; ++row)
    {
        pixels[row] = (tPixel *)malloc(pWidth * sizeof(tPixel));
    }

    printf("pixelAlloc\n"); 

    return pixels;
}

pBmp->pixel = BmpPixelAlloc(pBmp->infoHeader.width, pBmp->infoHeader.height);
if(FileRead(file, &pBmp->pixel, sizeof(tPixel), 1)!=0)
{ 
    errorCode = ErrorFileRead;
}
4

2 回答 2

4

您永远不应该对结构使用直接 I/O:仅仅因为您已经struct按照与 BMP 头文件相同的顺序声明了您的文件,因此无法保证您的编译器具有端到端的字段,中间没有任何内容。

编译器经常遵守平台的对齐限制,这可能会导致它们在字段之间添加填充字节以确保例如大字段的起始地址对齐。

您需要使用特定于编译器的魔法来强制将结构“打包”,或者将逐字节 I/O 放入结构中。

于 2013-11-02T19:13:19.373 回答
3

检查标题结构的填充。您可能会发现编译器已将 中的fileSizestruct与结构内的 4 字节边界对齐。如果您输入一些 debugprintf的结构元素地址,您可以看到这一点。

我在struct这里使用您的示例运行了一个示例:

typedef struct {
    byte        sigB;
    byte        sigM;
    int32_t     fileSize;
    int16_t     resv1;
    int16_t     resv2;
    int32_t     pixelOffset;
} tBmpHeader;

tBmpHeader hdr;

int main(int argc, char *argv[])
{
    printf("%d\n", sizeof(tBmpHeader) );
    printf("%p hdr\n", &hdr);
    printf("%p sigB\n", &hdr.sigB);
    printf("%p sigM\n", &hdr.sigM);
    printf("%p fileSize\n", &hdr.fileSize);
    printf("%p resv1\n", &hdr.resv1);
    printf("%p resv2\n", &hdr.resv2);
    printf("%p pixelOffset\n", &hdr.pixelOffset);
}

输出:

16
0x8049788 hdr
0x8049788 sigB
0x8049789 sigM
0x804978c fileSize
0x8049790 resv1
0x8049792 resv2
0x8049794 pixelOffset

hdr的开头和元素之间有一个 4 字节的偏移量fileSize,因此在 之前有两个字节的填充fileSize

要做的是一次读取标题一个日期项。您可以通过将读取封装在单个函数中来维护代码的某些“结构”(例如,“readBmpHeader(...)”)。您可以保留struct但单独阅读每个字段。所以,你会这样做(为了清楚起见,我省略了返回值检查):

FileRead(file, &pBmp->header.sigB, sizeof(byte), 1)
FileRead(file, &pBmp->header.sigB, sizeof(byte), 1)
FileRead(file, &pBmp->header.fileSize, sizeof(int32_t), 1)
FileRead(file, &pBmp->header.resv1, sizeof(int16_t), 1)
FileRead(file, &pBmp->header.resv2, sizeof(int16_t), 1)
FileRead(file, &pBmp->header.pixelOffset, sizeof(int32_t), 1)
于 2013-11-02T19:16:15.517 回答