C读取bmp文件

时间:2013-11-02 19:00:40

标签: c bmp

我正在尝试将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;
}

2 个答案:

答案 0 :(得分:4)

你永远不应该对结构使用直接I / O:只是因为你已经按照与BMP头相同的顺序声明了你的struct,所以不能保证你的编译器有端到端的字段。介于两者之间。

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

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

答案 1 :(得分:3)

检查标题结构的填充。您可能会发现编译器已将fileSize中的struct值与结构中的4字节边界对齐。如果你输入结构元素地址的一些调试printf,你可以看到这个。

我在这里使用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的开头和fileSize元素之间有一个4字节的偏移量,因此在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)