BMP 宽度/高度尺寸超过 255?

Posted

技术标签:

【中文标题】BMP 宽度/高度尺寸超过 255?【英文标题】:BMP Width/Height size over 255? 【发布时间】:2014-05-12 10:44:06 【问题描述】:

众所周知,每个字节都是无符号字符,这意味着范围为 0 - 255。 我写了一个可以编写 BMP 的代码,该代码可以工作。问题是当我尝试编写一个宽度/高度大于 255 的 bmp 时,它显示为小于 255 的值。

在宽度的下一点上,我刚刚输入了1,它可以工作。它用 (500) 输出实数,但我希望它也适用于高度.. 但事实并非如此。图像坏了。

更新:高度超过 214 会导致 bmp 损坏。我想念什么? 这是我的代码:

typedef unsigned char  BYTE;    //1
typedef unsigned short WORD;    //2
typedef unsigned long  DWORD;   //4

typedef struct BMP_HEADER

    BYTE bmp_type[2];       //2
    BYTE bmp_size[4];       //4

    BYTE bmp_as[4];         //4

    BYTE bmp_offset[4];     //4
 BMP;

typedef struct DIB_HEADER

    BYTE dib_size[4];       //4
    DWORD dib_BMPwidth;     //4
    DWORD dib_BMPheight;        //4

    BYTE dib_BMPcplanes[2];     //2
    BYTE dib_BMPBPX[2];     //2
    BYTE dib_BIRGB[4];      //4
    BYTE dib_rawsize[4];        //4

    BYTE dib_Xresolution[4];    //4
    BYTE dib_Yresolution[4];    //4

    BYTE dib_cpalette[4];       //4
    BYTE dib_cimportant[4];     //4
 DIB;

typedef struct PIXEL_ARRAY

    BYTE BGR[3];            //3
 PIX;

BYTE* PAD;
DWORD padding(DWORD BMPwidth)

    int pitch = BMPwidth * 3;

    if (pitch % 4 != 0)
    
        pitch += 4 - (pitch % 4);
    

    return pitch - (BMPwidth * 3);


void create_bmp(char BMPname[], DWORD BMPwidth, DWORD BMPheight, WORD BMPR, WORD BMPG, WORD BMPB)

    // variables
    char build_name[256];
    FILE* fp;

    BMP newBMP;
    DIB newDIB;
    PIX* template;

    gedMaxGameMem = 2827465479;
    if(BMPwidth > 1 && BMPheight > 1) template = (PIX*)malloc(BMPwidth*BMPheight);
    else template = (PIX*)malloc(sizeof(PIX*)*max(BMPwidth, BMPheight));

    // make file
    sprintf(build_name, "%s.bmp", BMPname);
    fp = fopen(build_name, "wb");

    // BM
    newBMP.bmp_type[0] = 'B';
    newBMP.bmp_type[1] = 'M';   
    fwrite(&newBMP.bmp_type, 1, 2, fp);

    
    // SIZE
    DWORD sz = (sizeof(newBMP) + sizeof(newDIB) + sizeof(template));
    fwrite(&sz, 1, 1, fp);
    

    // Application specifics
    fwrite(&newBMP.bmp_as, 1, 4, fp);

    // offset
    newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
    fwrite(&newBMP.bmp_offset, 1, 4, fp);

    //

    // DIB SIZE
    newDIB.dib_size[0] = sizeof(newDIB);
    fwrite(&newDIB.dib_size, 1, 4, fp);

    // DIB Image width
    newDIB.dib_BMPwidth = BMPwidth;
    fwrite(&newDIB.dib_BMPwidth, 1, 4, fp);

    // DIB Image height
    newDIB.dib_BMPheight = BMPheight;
    fwrite(&newDIB.dib_BMPheight, 1, 4, fp);

    // DIB Color planes
    newDIB.dib_BMPcplanes[0] = 1;
    fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);

    // DIB BMP Bits per pixel
    newDIB.dib_BMPBPX[0] = 24;
    fwrite(&newDIB.dib_BMPBPX, 1, 2, fp);

    // DIB Compression method
    fwrite(&newDIB.dib_BIRGB, 1, 4, fp);

    // DIB Raw size
    newDIB.dib_rawsize[0] = sizeof(template);
    fwrite(&newDIB.dib_rawsize, 1, 4, fp);

    // DIB Xresolution
    newDIB.dib_Xresolution[0] = 19;
    newDIB.dib_Xresolution[1] = 11;
    fwrite(&newDIB.dib_Xresolution, 1, 4, fp);

    // DIB Yresolution
    newDIB.dib_Yresolution[0] = 19;
    newDIB.dib_Yresolution[1] = 11;
    fwrite(&newDIB.dib_Yresolution, 1, 4, fp);

    // DIB Colors palette
    fwrite(&newDIB.dib_cpalette, 1, 4, fp);

    // DIB Important colors
    fwrite(&newDIB.dib_cimportant, 1, 4, fp);

    //

    
        int i;

        for(i=0; i<BMPwidth+1; i++)
        
            template[i].BGR[0] = BMPB;
            template[i].BGR[1] = BMPG;
            template[i].BGR[2] = BMPR;

            fwrite(template->BGR, 3, i, fp);
        
    

    free(template);
    fclose(fp);

【问题讨论】:

您似乎正在写入许多未初始化的值。例如,我看不到您将 newDIB 结构归零。您只需设置一些字段,例如 dib_BMPheight[0](有时还有 dib_BMPheight[1]),但不设置 dib_BMPheight[2] 和 dib_BMPheight[3]。然后你把它们写出来,所以它们可能是垃圾。 还要注意步幅可能与宽度不匹配。 (步幅是从一个扫描线到下一个扫描线的字节数。)每个扫描线必须与一个四字节边界对齐。那就是步幅必须是 4 的倍数。由于每个像素写入 3 个字节,因此只有当宽度是 4 的倍数时,步幅才匹配宽度*3。 “未初始化”值是什么意思?为什么我必须将 newDIB 结构“归零”,我使用 fwrite 来写入我想要的字节。而且宽度现在也有效,高度现在给我带来了问题。如果步幅日志。东西可以帮助我..我怎样才能用它来指定高度的步幅? newDIB.dib_size[0] = sizeof(newDIB); fwrite(&amp;newDIB.dib_size, 1, 4, fp); 将四个字节写入文件,但您只设置了其中一个。其他三个未初始化。当您在堆栈上实例化一个变量时,它的值可以是任何值,直到您将其显式设置为某个值。如果变量是一个结构,那么你必须在使用它们之前初始化所有其中的字段。 我忘记了,谢谢 :) 即便如此.. 我在审查 BMP 结构的 1 小时内成功地做了一些事情,而且我的英语甚至很糟糕。现在我认为我对更改的初始化程序和使用的填充没有问题。 【参考方案1】:

如果你想把宽度和高度作为字节来摆弄,你不能只将第二个字节设置为1,你必须计算出确切的值,例如:

uint32_t h = 1024;

height[0] = h & 0xff;
height[1] = (h >> 8) & 0xff;
height[2] = (h >> 16) & 0xff;
height[3] = (h >> 24) & 0xff;

(这里用&amp; 0xff 掩码真的是不必要的,反正在将数字拟合成一个字节时已经完成了。)

但也许你应该首先在你的结构中使用更大的整数类型:

typedef struct DIB_HEADER

    DWORD dib_size;
    DWORD dib_BMPwidth;
    DWORD dib_BMPheight;
    WORD dib_BMPcplanes];
    ...
;

(这可能会导致字节序问题,但我认为 Windows 位图以 Little-Endian 格式存储,因此这里不应该成为问题。)

编辑:进一步查看您的代码,我发现一些错误:

如果要为像素分配临时内存,则必须为每个像素分配 3 个字节,即sizeof(PIX) * BMPwidth + BMWheight。您对单行、单列和其他位图的区分毫无意义,并且还引入了错误(即没有为每个像素分配每个像素的大小。) 您正在编写仅一种颜色的单色位图,因此您实际上不需要创建巨大的临时位图;您可以一遍又一遍地写入相同的像素。 您在堆栈上创建BMPDIB 结构;因此,它们可能未初始化,并且在您未明确初始化的字段中有垃圾。 您不必单独编写每个字段,而是可以编写整个结构。 (但是您必须去掉"BM" 标记,因为结构将在那里填充以使下一个字段从四字节边界开始。您还可以使用编译器选项来始终紧紧地打包结构。 ) 当您写出数据时,您只循环宽度,但您必须在嵌套循环中同时循环宽度和高度。 当你用fwrite 写出数据时,你的论点是错误的:你可能想写fwrite(&amp;template[i]-&gt;RGB, 3, 1, fp)。但是您的项目计数是i,这意味着您一遍又一遍地从头开始编写所有内容,但始终附加最后一个字节,例如“AABAABCABCDABCDE”。 (当所有像素都具有相同的颜色时,这无关紧要,但您的文件大小将会关闭。) BMP 行数据填充为 4 个字节。您为此编写了一个例程,但不要调用它。

换句话说。您的代码没有为超过一行的数据做好准备。 :-)

这是编写单色位图的代码的更简单版本:

void create_bmp_uni(const char *fn, int w, int h, int r, int g, int b)

    FILE* fp;
    DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
    DWORD pad = rsize - w * sizeof(PIX);
    DWORD rawsize = rsize * h * sizeof(PIX);
    BYTE zero[3] = 0;
    const char *id = "BM";

    BMP bmp = 
        2 + sizeof(BMP) + sizeof(DIB) + rawsize, 
        0,
        2 + sizeof(BMP) + sizeof(DIB)
    ;

    DIB dib = 
        sizeof(DIB),
        w,
        h,
        1,
        24,
        0,
        rawsize,
        2835,
        2835,
        0,
        0
    ;
    PIX pix = b, g, r;
    int i, j;

    fp = fopen(fn, "wb");
    fwrite(id, 1, 2, fp);
    fwrite(&bmp, 1, sizeof(bmp), fp);
    fwrite(&dib, 1, sizeof(dib), fp);

    for(i = 0; i < h; i++) 
        for(j = 0; j < w; j++) 
            fwrite(&pix, sizeof(pix), 1, fp);
        
        if (pad) fwrite(zero, 1, pad, fp);
    

    fclose(fp);

.bmp 后缀必须由调用者添加。还要注意 BM 标记是如何与 BMP 结构的其余部分分开编写的。)

【讨论】:

顺便提一下,我使用的是非常古老的编译器,我不知道它是否甚至在 C89 下并且不包括 uint32。 从 BYTE 更改为 DWORD 仅适用于宽度。如果我用高度这样做,它就不会绘制图像。 固定长度的整数类型在&lt;stdint.h&gt;中定义。但由于您使用的是 Windows,因此您可以使用 DWORD 代替 'uint32_t` 和 WORD 代替 uint16_t 是的,我明白了:)。但是身高还是不想出来。将高度弄乱超过214 会导致 bmp 损坏。 但这与结构字段中不正确的高度无关。您的内存分配以及写出数据的方式不正确。我会更新我的答案【参考方案2】:

输出: http://i.stack.imgur.com/fqxO9.png(每次都有不同的颜色) 当前代码:

typedef unsigned char  BYTE;    //1
typedef unsigned short WORD;    //2
typedef unsigned long  DWORD;   //4

typedef struct BMP_HEADER

    BYTE bmp_size[4];       //4

    BYTE bmp_as[4];         //4

    BYTE bmp_offset[4];     //4
 BMP;

typedef struct DIB_HEADER

    BYTE dib_size[4];           //4
    DWORD dib_w;                //4
    BYTE dib_h[4];              //4

    BYTE dib_BMPcplanes[2];     //2
    BYTE dib_BMPBpix[2];            //2
    BYTE dib_BIRGB[4];          //4
    BYTE dib_rawsize[4];        //4

    BYTE dib_Xresolution[4];    //4
    BYTE dib_Yresolution[4];    //4

    BYTE dib_cpalette[4];       //4
    BYTE dib_cimportant[4];     //4
 DIB;

//_______________________________________________________________________________________________//
typedef struct PIXEL_ARRAY

    BYTE BGR[3];            //3
 PIX;

BYTE* PAD;
DWORD padding(DWORD w)

    int pitch = w * 3;

    if (pitch % 4 != 0)
    
        pitch += 4 - (pitch % 4);
    

    return pitch - (w * 3);


DWORD row_sz(WORD BMPBpix, DWORD w)

    return ((BMPBpix*w+31)/32)*4;


void create_bmp(char BMPname[], DWORD w, DWORD h, BYTE R, BYTE G, BYTE B)

    // variables
    char build_name[256];
    FILE* fp;

    BMP newBMP;
    DIB newDIB;
    PIX* pix;

    DWORD rsize = (w * sizeof(PIX) + 3) / 4 * 4;
    DWORD pad = rsize - w * sizeof(PIX);
    DWORD rawsize = rsize * h * sizeof(PIX);
    BYTE zero[3] = 0;
    const char *id = "BM";
    int i, j;

    gedMaxGameMem = 2827465479;
    if(w > 1 && h > 1) pix = (PIX*)malloc(w*h);
    else pix = (PIX*)malloc(sizeof(PIX*)*max(w, h));

    // make file
    sprintf(build_name, "%s.bmp", BMPname);
    fp = fopen(build_name, "wb");

    // BM
    fwrite(id, 1, 2, fp);

    // SIZE
    newBMP.bmp_size[0] = (sizeof(newBMP) + sizeof(newDIB) + sizeof(pix));
    fwrite(&newBMP.bmp_size, 1, 4, fp);

    // Application specifics
    fwrite(&newBMP.bmp_as, 1, 4, fp);

    // offset
    newBMP.bmp_offset[0] = (sizeof(newBMP) + sizeof(newDIB));
    fwrite(&newBMP.bmp_offset, 1, 4, fp);

    //

    // DIB SIZE
    newDIB.dib_size[0] = sizeof(newDIB);
    fwrite(&newDIB.dib_size, 1, 4, fp);

    // DIB Image width
    newDIB.dib_w = w;
    fwrite(&newDIB.dib_w, 1, 4, fp);

    // DIB Image height
    newDIB.dib_h[0] = h;
    fwrite(&newDIB.dib_h, 1, 4, fp);

    // DIB Color planes
    newDIB.dib_BMPcplanes[0] = 1;
    fwrite(&newDIB.dib_BMPcplanes, 1, 2, fp);

    // DIB BMP Bits per pixel
    newDIB.dib_BMPBpix[0] = 24;
    fwrite(&newDIB.dib_BMPBpix, 1, 2, fp);

    // DIB Compression method
    fwrite(&newDIB.dib_BIRGB, 1, 4, fp);

    // DIB Raw size
    newDIB.dib_rawsize[0] = sizeof(pix);
    fwrite(&newDIB.dib_rawsize, 1, 4, fp);

    // DIB Xresolution
    newDIB.dib_Xresolution[0] = 19;
    newDIB.dib_Xresolution[1] = 11;
    fwrite(&newDIB.dib_Xresolution, 1, 4, fp);

    // DIB Yresolution
    newDIB.dib_Yresolution[0] = 19;
    newDIB.dib_Yresolution[1] = 11;
    fwrite(&newDIB.dib_Yresolution, 1, 4, fp);

    // DIB Colors palette
    fwrite(&newDIB.dib_cpalette, 1, 4, fp);

    // DIB Important colors
    fwrite(&newDIB.dib_cimportant, 1, 4, fp);

    //

    for(i = 0; i < h; i++) 
    
        for(j = 0; j < w; j++) 
        
            fwrite(&pix, sizeof(pix), 1, fp);
        
        if (pad) fwrite(zero, 1, pad, fp);
    
    fclose(fp);

【讨论】:

以上是关于BMP 宽度/高度尺寸超过 255?的主要内容,如果未能解决你的问题,请参考以下文章

在 C++ 中读取 bmp 文件的宽度和高度

我尝试用 c++ 编写一个 bmp 文件,但是 bmp 文件的宽度和高度似乎会影响结果,有时我得到了一个错误的 bmp 文件

尺寸,只改变宽度/高度

用C语言编写程序处理图片bmp文件 1.读取图片的宽度,高度,每个像素所需的位数,水平分辨率,垂直

使用相机查找对象的尺寸(高度、宽度)

获取 YouTube 视频尺寸(宽度/高度)