简体   繁体   English

C读取bmp文件

[英]C reading bmp files

I'm trying to read in a bmp file to my program and I'm having some issues. 我正在尝试将bmp文件读入我的程序,我遇到了一些问题。 After reading in the file if i tell it to print the pBmp->header.fileSize it says 16 but if i look at it in a hex editor the file size portion has F6 7A 10 00 if I modify the value to the correct file size in the hex it will say F6 7A F6 7A 10 00 but that is running into the resv1 which should always be zero. 如果我告诉它打印pBmp-> header.fileSize,那么在读取文件后它会显示16但是如果我在十六进制编辑器中查看它,如果我将值修改为正确的文件大小,文件大小部分有F6 7A 10 00在十六进制中它会说F6 7A F6 7A 10 00但是它会进入resv1,它应该始终为零。 I know that this is only reading in 1 pixel of data another issue I have is when I try to use a while loop to read in the pixels until the end of file I get a segmentation fault. 我知道这只是读取1像素数据的另一个问题,当我尝试使用while循环读取像素直到文件结束时我得到分段错误。 I've literally spent hours googling trying to figure this out but I'm not having much luck. 我花了几个小时谷歌搜索试图解决这个问题,但我没有太多运气。

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

You should never use direct I/O with structures: just because you've declared your struct in the same order as the BMP headers, there's no guarantee that your compiler has the fields end-to-end with nothing in between. 您永远不应该对结构使用直接I / O:只是因为您已经按照与BMP标头相同的顺序声明了struct ,所以无法保证编译器的端到端字段之间没有任何内容。

Compilers frequently adhere to a platform's alignment restrictions, which might cause them to add padding bytes between fields to make sure eg a large field's starting address is aligned. 编译器经常遵循平台的对齐限制,这可能导致它们在字段之间添加填充字节以确保例如大字段的起始地址对齐。

You need to either use compiler-specific magic to force the structure to be "packed", or byte-by-byte I/O into the structure. 您需要使用特定于编译器的魔法来强制结构“打包”,或逐字节I / O进入结构。

Check the padding of the structure for the header. 检查结构的填充以获取标题。 You may find that the compiler has aligned the fileSize value in the struct to a 4-byte boundary within the structure. 您可能会发现编译器已将structfileSize值与struct中的4字节边界对齐。 You can see this if you put in some debug printf 's of the structure element addresses. 如果你输入一些结构元素地址的调试printf你可以看到这个。

I ran an example using your struct here: 我在这里使用你的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);
}

With the output: 随着输出:

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

There is a 4-byte offset between the beginning of hdr and the fileSize element, so there are two bytes of padding before fileSize . hdr的开头和fileSize元素之间有一个4字节的偏移量,因此在fileSize之前有两个字节的填充。

The thing to do is read the header a date item at a time. 要做的是一次读取标题日期项目。 You can maintain some "structure" to the code by encapsulating the reads in a single function (eg, "readBmpHeader(...)"). 您可以通过将读取封装在单个函数中来维护代码的某些“结构”(例如,“readBmpHeader(...)”)。 You can keep the struct but read each field separately. 您可以保留struct但分别读取每个字段。 So, you would do (I leave off the return value checks for clarity of example): 所以,你会这样做(我为了清晰的例子而留下了返回值检查):

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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM