C误读.bmp文件的解析

Zap*_*Tap 2 c gcc bmp

我有一个项目,涉及将.bmp文件读入C程序,在其上放置一个掩码,并将带有掩码的版本打印回另一个文件.我遇到问题的部分似乎是在文件中读取的实际过程.我看到的第一个大红旗是它以错误的分辨率继续阅读.我搜索了很多,看到一些脚本在.bmp文件中读取作为各种问题的答案,但使用这些脚本中的逻辑没有帮助.

主要问题似乎是,不是在我教授给出的示例图像中读取200 x 300的正确尺寸,而是读入13107200 x 65536.但是,如果我要将打印的代码部分包括在内不同的文件,您会看到输出文件具有适当的分辨率.这告诉我,我可能正确地阅读信息,但不能以我认为的方式存储它.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct HEADER {
    unsigned short int Type;        // Magic indentifier
    unsigned int Size;              // File size in bytes
    unsigned short int Reserved1, Reserved2;
    unsigned int Offset;            // Offset to data (in B)
} Header;                           // -- 14 Bytes

struct INFOHEADER {
    unsigned int Size;              // Header size in bytes
    int Width, Height;              // Width / height of image
    unsigned short int Planes;      // Number of colour planes
    unsigned short int Bits;        // Bits per pixel
    unsigned int Compression;       // Compression type
    unsigned int ImageSize;         // Image size in bytes
    int xResolution, yResolution;   // Pixels per meter
    unsigned int Colors;            // Number of colors
    unsigned int ImportantColors;   // Important colors
} InfoHeader;                       // -- 40 Bytes

struct PIXEL {
    unsigned char Red, Green, Blue; // Intensity of Red, Green, and Blue
};                                  // -- 3 Bytes

int getFileSize(FILE *input);
int getHexVal(FILE *input);
struct HEADER *getHeader(FILE *input);
struct INFOHEADER *getInfoHeader(FILE *input);
struct PIXEL *getPixel(FILE *input, struct PIXEL *loc);
struct HEADER *printHeader(FILE *output);
struct INFOHEADER *printInfoHeader(FILE *output);
struct PIXEL *printPixel(FILE *output, struct PIXEL *loc);

int main(int argc, char const *argv[]) {
    if (argc == 3) {
         if (!strcmp(argv[1], argv[2])) {
            printf("The input and output file must be different. Please try again.\n");
            return 1;
        }

        // char Matrix[3][3] =
        // { {  0, -1,  0 },
        //   { -1,  4, -1 },
        //   {  0, -1,  0 }
        // };

        FILE *input = fopen(argv[1], "rb");
        if (!input) return 1;
        int i, j;
        // getHeader(input);
        fread(&Header, sizeof(struct HEADER), 1, input);
        if (Header.Type != 0x4D42) {
            printf("The specified input file was not a bitmap. Please try again.");
            fclose(input);
            return 1;
        }
        // getInfoHeader(input);
        fread(&InfoHeader, sizeof(struct INFOHEADER), 1, input);
        fseek(input, Header.Offset, SEEK_SET);
        struct PIXEL arr[InfoHeader.Width][InfoHeader.Height];

        printf("%d %d\n", InfoHeader.Width, InfoHeader.Height);
        for (i = 0; i < InfoHeader.Width; i++) {
            for (j = 0; j < InfoHeader.Height; j++) {
                getPixel(input, arr[i] + j);
                printf("%d %d %d\n", arr[i][j].Red, arr[i][j].Green, arr[i][j].Blue);
            }
        }
        fclose(input);
    }
}
Run Code Online (Sandbox Code Playgroud)

Dav*_*nan 6

我可以看到你的代码有很多问题:

1.数据类型的大小不一致

在不同的平台上,类型int和类型short可以有不同的大小.因此,int在一个平台上可能是一个尺寸,在另一个平台上可能是另一个尺寸.您可能需要使用精确大小的类型uint32_t.

2.填充和对齐

存储在文件中的标头已打包.你的结构是对齐的.这意味着编译器在成员之间插入填充以确保成员始终对齐以获得最佳内存访问.

有多种方法可以解决这个问题.您可以声明要打包的结构.这会让你到目前为止,但看到下一点.

3.字节序

如果您正在大端系统上读取Windows位图,则可以将文件中的小端数据转换为系统的大端数据.

4. xResolution,yResolution是错误的成员

这些用于表示像素的物理尺寸.在实践中,它们很少被指定.你打算阅读WidthHeight.

5. VLA(哇!)

您正在使用可变长度数组:struct PIXEL arr[InfoHeader.xResolution][InfoHeader.yResolution].这很容易导致大位图的堆栈溢出.您确实需要为像素阵列使用动态分配的内存.


我该如何处理这些问题?

  • 使用确切大小的类型.
  • 声明打包的结构.
  • 从文件中读取结构,然后根据需要执行字节序更正.
  • 分配像素阵列malloc.


Nis*_*röm 5

类型intshort仅保证具有某些最小尺寸.它们可以根据不同的实现而变化 即使我们假设a int和分别short是四个和两个八位字节,在读取和编写结构时仍然会遇到问题.

例如:

struct HEADER {
  unsigned short int Type;
  unsigned int Size;
  unsigned short int Reserved1, Reserved2;
  unsigned int Offset;
} Header;
Run Code Online (Sandbox Code Playgroud)

为了使Size处理器适当地对齐,编译器将(通常)在偏移+4而不是+2(假设上面提到的大小)之间插入填充TypeSize哪些位置Size.

读取(和写入)二进制格式的最佳方法是将文件读入unsigned char *缓冲区,然后从那里提取字段.例如.

unsigned long Size = buffer[2] +
                     buffer[3] * 0x100UL +
                     buffer[4] * 0x10000UL +
                     buffer[5] * 0x1000000UL;
Run Code Online (Sandbox Code Playgroud)

或类似的.

  • `x << 8UL`不会将`x`提升为unsigned long,因此这段代码不起作用(除非`int`和`long`的大小相同).你可以做`((unsigned long)buffer [3])<< 8`,或者最好是我,`buffer [3]*0x100UL` (2认同)