C++ 在8位BMP中查找颜色表的位置

C++ 在8位BMP中查找颜色表的位置,c++,C++,我明白这可能真的很简单,但在花了大量时间研究之后,我正在努力找到一个明确的答案 我正在编写一些代码,在带有TFT显示器的嵌入式系统上打开并显示BMP。我在显示16位、24位和32位BMP时没有问题,但我正在努力使用8位BMP。我的问题是,我不确定颜色表在bmp文件中的确切起始位置。看着 颜色表应该放在文件中50个字节(因为我的文件有一个40字节的dib头)+12个字节,即总共62个字节。当我尝试为从该区域读取的表格编制索引时,我看到一个类似于bmp的图像,但颜色完全错误 如何准确确定颜色表的正确

我明白这可能真的很简单,但在花了大量时间研究之后,我正在努力找到一个明确的答案

我正在编写一些代码,在带有TFT显示器的嵌入式系统上打开并显示BMP。我在显示16位、24位和32位BMP时没有问题,但我正在努力使用8位BMP。我的问题是,我不确定颜色表在bmp文件中的确切起始位置。看着

颜色表应该放在文件中50个字节(因为我的文件有一个40字节的dib头)+12个字节,即总共62个字节。当我尝试为从该区域读取的表格编制索引时,我看到一个类似于bmp的图像,但颜色完全错误

如何准确确定颜色表的正确位置

当我查看文件中的位置0x1C时,值为8表示8bpp,但当我查看0x2E时,值为0,不应该是256吗

我读取和显示位图的代码如下所示:

typedef struct _BGRA {
unsigned char Blue;
unsigned char Green;
unsigned char Red;
unsigned char Alpha;
} BGRA;

int CBitmapViewerDlg::display_bmp(unsigned char *bmp_data, unsigned short left, 

unsigned short top, CDC *dc)
{
    unsigned short temp = 0;
    unsigned short width = 480, height = 272;
    unsigned short x = 0,  y = 0, idx = 0;
    unsigned char byte_width = 0;
    unsigned short data_offset = 0;
    unsigned short u16bpp = 8;


    width = bmp_data[0x12];
    width |= (bmp_data[0x13] << 8);
    width |= (bmp_data[0x14] << 16);
    width |= (bmp_data[0x15] << 24);

    height = bmp_data[0x16];
    height |= bmp_data[0x17] << 8;
    height |= bmp_data[0x18] << 16;
    height |= bmp_data[0x19] << 24;

    data_offset = bmp_data[0x0A];
    data_offset |= bmp_data[0x0B] << 8;
    data_offset |= bmp_data[0x0C] << 16;
    data_offset |= bmp_data[0x0D] << 24;

    BGRA *colour_table_offset = (BGRA *)bmp_data + 0x3E;// [0x3E];

    u16bpp = bmp_data[0x1C];
    u16bpp |= bmp_data[0x1D];

    bmp_data += data_offset;

    if (u16bpp == 8)
    {
        unsigned char r, g, b;
        for (y=height; y>0; y--)
        {           
            for(x=0;x<width;x++)
            {
                b = colour_table_offset[*bmp_data].Blue;
                g = colour_table_offset[*bmp_data].Green;
                r = colour_table_offset[*bmp_data].Red;             
                dc->SetPixel(x, y, RGB(r, g, b));                               
                bmp_data++;
            }           

        }
    }
}
{ 无符号短x=0,y=0

mBitmapHeader *bmHdr;
bmHdr = (mBitmapHeader*)bmp_data;

BGRA *colour_table_offset;
colour_table_offset = (BGRA*)bmp_data + (sizeof(mBitmapFileHdr_t) + sizeof(mBitmapInfoHdr_t));

int bytesPerRow = bmHdr->info.biWidth + (bmHdr->info.biWidth%2);    // rows must be a multiple of 2 bytes

if (bmHdr->info.biBitCount == 8)
{
    for (y=0; y<bmHdr->info.biHeight; y++)
    //for (y=bmHdr->info.biHeight; y>0; y--)
    {           
        uint8_t *curRow;
        curRow = (uint8_t*) (bmHdr->hdr.bfOffsetBits + (y*bytesPerRow) + bmp_data);
        for(x=0;x<bmHdr->info.biWidth;x++)
        {
            uint32_t palIndex;
            palIndex = curRow[x];
            BGRA curCol = colour_table_offset[palIndex];

            uint8_t r, g, b;
            b = (curCol>>0) & 0xFF;
            g = (curCol>>8) & 0xFF;
            r = (curCol>>16) & 0xFF;
            dc->SetPixel(x, y, RGB(r,g,b) );
        }                       
    }
}

return 1;
mBitmapHeader*bmHdr;
bmHdr=(mBitmapHeader*)bmp_数据;
BGRA*颜色表偏移量;
颜色表偏移量=(BGRA*)bmp数据+(sizeof(mBitmapFileHdr_t)+sizeof(mBitmapInfoHdr_t));
int bytesPerRow=bmHdr->info.biWidth+(bmHdr->info.biWidth%2);//行必须是2字节的倍数
如果(bmHdr->info.biBitCount==8)
{
对于(y=0;yinfo.biHeight;y++)
//对于(y=bmHdr->info.biHeight;y>0;y--)
{           
uint8_t*curRow;
curRow=(uint8_t*)(bmHdr->hdr.bOffsetBits+(y*字节数)+bmp_数据);
for(x=0;xinfo.biWidth;x++)
{
uint32_t palIndex;
palIndex=curRow[x];
BGRA curCol=颜色表偏移量[palIndex];
uint8_t r,g,b;
b=(curCol>>0)和0xFF;
g=(curCol>>8)&0xFF;
r=(curCol>>16)&0xFF;
dc->SetPixel(x,y,RGB(r,g,b));
}                       
}
}
返回1;
}


Andrew

公平地说,BMP格式的文档可能有点混乱。多年来的各种添加/定制显然对这种情况没有任何帮助

简而言之,颜色表或调色板的偏移量为54。假设在Windows下使用Gimp保存了一个图像,我今天播放的4、8和24位图像都是这样

访问文件头的各个字段的一种更简单的方法是使用结构。请注意,编译器可能会尝试填充结构,以便硬件能够更轻松地加载任何给定字段。您可以使用编译器指令来防止这种情况。在我的例子中,我使用的是g++,最小的元素是2个字节,所以我使用编译器指令对:(在structs之前)
#pragma pack(push,2)
和(在structs之后)
#pragma pop
。其他编译器可能会有所不同,我不记得了。几乎任何人都能写出比我更好的结构填充解释,我鼓励你们去寻找一个

当我查看文件中的位置0x1C时,值为8表示8bpp,但当我查看0x2E(46d)时,值为0,不应该是256吗

好的,如果您查看BMP的格式规范,例如(请参阅Windows BitMapInfo Header的表格)并查看此字段的说明,您可以看到,对于具有颜色表的图像,它是(a)颜色数或(b)0,在这种情况下,假定为2^bitsPerPixel。 因此,对于8位图像,如果该值为0,则您知道有2^8=256种颜色-这是您期望的值。不知道为什么会做出这样的决定,您只需要添加额外的一点逻辑来获取/计算要读取/处理的调色板值的数量

考虑以下代码:

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

/*
 2 x 2 bitmap (2x2.bmp)
 4bit indexed color - leftmost pixel in hi-nibble
 red, green
 blue, white
 78 bytes

42 4D - 4E 00 00 00 - 00 00 - 00 00 - 46 00 00 00 - 28 00
00 00 - 02 00 00 00 - 02 00 00 00 - 01 00 - 04 00 - 00 00
00 00 - 08 00 00 00 - 13 0B 00 00 - 13 0B 00 00 - 04 00
00 00 - 04 00 00 00 - FF 00 00 00 - 00 00 FF 00 - 00 FF
00 00 - FF FF FF 00 - 03 00 00 00 - 12 00 00 00
*/

/*
//mBitmapFileHdr_t
42 4D           'BM'
4E 00 00 00     78
00 00            0
00 00            0
46 00 00 00     70
//mBitmapInfoHdr_t
28 00 00 00     40
02 00 00 00      2
02 00 00 00      2
01 00            1
04 00            4
00 00 00 00      0
08 00 00 00      8
13 0B 00 00   2835
13 0B 00 00   2835
04 00 00 00 - cols in table
04 00 00 00 - important cols in table
// Colour-map data
FF 00 00 00     - 0 blue
00 00 FF 00     - 1 red
00 FF 00 00     - 2 green
FF FF FF 00     - 3 white
// Pixel data - bottom -> top in this image
03 00 00 00 - blue, white
12 00 00 00 - red, green
*/
#pragma pack(push,2)
typedef struct mBitmapFileHdr_t
{
    uint16_t bfType;
    uint32_t bfSize;
    uint16_t bfReserved1;
    uint16_t bfReserved2;
    uint32_t bfOffsetBits;
} ;

typedef struct mBitmapInfoHdr_t
{
    uint32_t biSize;
    uint32_t biWidth;
    uint32_t biHeight;
    uint16_t biPlanes;
    uint16_t biBitCount;
    uint32_t biCompression;
    uint32_t biSizeImage;
    uint32_t biXPelsPerMeter;
    uint32_t biYPelsPerMeter;
    uint32_t biClrUsed;
    uint32_t biClrImportant;
};

typedef struct mBitmapHeader
{
    mBitmapFileHdr_t hdr;
    mBitmapInfoHdr_t info;
};
#pragma pop

void dispBmpInfo(char *filename)
{
    FILE *fp;
    long fileSize;
    char *rawData;

    fp = fopen(filename, "rb");
    fseek(fp, 0, SEEK_END);
    fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    rawData = (char*)malloc( fileSize * sizeof(char) );
    fread(rawData, sizeof(char), fileSize, fp);

    mBitmapHeader *bmHdr;
    bmHdr = (mBitmapHeader*)rawData;

    printf("Width: %d\n", bmHdr->info.biWidth);
    printf("Height: %d\n", bmHdr->info.biHeight);
    printf("Planes: %d\n", bmHdr->info.biPlanes);
    printf("Bits/Pixel: %d\n", bmHdr->info.biBitCount);
    printf("Colors used: %d\n", bmHdr->info.biClrUsed);
    printf("Important Colors: %d\n", bmHdr->info.biClrImportant);
    printf("Offset of pallete: %d\n", sizeof(mBitmapFileHdr_t) + sizeof(mBitmapInfoHdr_t));
    printf("Offset of colour data: %d\n", bmHdr->hdr.bfOffsetBits);

    free(rawData);
    fclose(fp);
}

int main()
{
    dispBmpInfo("2x2.bmp");
    return 0;
}
应用于24位bmp时的输出

Width: 243
Height: 61
Planes: 1
Bits/Pixel: 24
Colors used: 0
Important Colors: 0
Offset of pallete: 54
Offset of colour data: 54
Width: 243
Height: 61
Planes: 1
Bits/Pixel: 8
Colors used: 256
Important Colors: 256
Offset of pallete: 54
Offset of colour data: 1078
应用于上述bmp的8位版本时输出

Width: 243
Height: 61
Planes: 1
Bits/Pixel: 24
Colors used: 0
Important Colors: 0
Offset of pallete: 54
Offset of colour data: 54
Width: 243
Height: 61
Planes: 1
Bits/Pixel: 8
Colors used: 256
Important Colors: 256
Offset of pallete: 54
Offset of colour data: 1078
编辑:用于显示添加的位图的代码

typedef uint32_t BGRA;

void dispBmp(char *filename, HDC hdc)
{
    FILE *fp;
    long fileSize;
    char *rawData;

    fp = fopen(filename, "rb");
    fseek(fp, 0, SEEK_END);
    fileSize = ftell(fp);
    fseek(fp, 0, SEEK_SET);

    rawData = (char*)malloc( fileSize * sizeof(char) );
    fread(rawData, sizeof(char), fileSize, fp);

    mBitmapHeader *bmHdr;
    bmHdr = (mBitmapHeader*)rawData;

    BGRA *colour_table_offset;
    colour_table_offset = (BGRA*) (rawData + (sizeof(mBitmapFileHdr_t) + sizeof(mBitmapInfoHdr_t)));

    int bytesPerRow = bmHdr->info.biWidth + (bmHdr->info.biWidth%2);    // rows must be a multiple of 2 bytes

    int y, x;
    for (y=0; y<bmHdr->info.biHeight; y++)
    {
        uint8_t *curRow;
        curRow = (uint8_t*) (bmHdr->hdr.bfOffsetBits + (y*bytesPerRow) + rawData);
        for (x=0; x<bmHdr->info.biWidth; x++)
        {
            uint32_t palIndex;
            palIndex = curRow[x];
            BGRA curCol = colour_table_offset[palIndex];
            SetPixel(hdc, x, y, curCol);
        }
    }
    free(rawData);
    fclose(fp);
}

位图文件头始终为14字节。除了验证格式以确保您拥有有效的BMP文件外,您还可以安全地跳过它

位图信息头紧跟其后,可以有一个可变的大小,尽管到目前为止最常见的是40字节。您可以使用从偏移量14开始的4个字节来确定大小:

uint32_t header_size = (bmp_data[14] & 0xff) | ((bmp_data[15] & 0xff) << 8) | ((bmp_data[16] & 0xff) << 16) | ((bmp_data[17] & 0xff) << 24);

如果您发布非常小的bmp文件的十六进制转储,以及您用来读取它的代码,这可能会有所帮助。如果我没有记错的话,颜色表紧跟在位图信息头之后。我曾尝试上载原始bmp和十六进制转储,但我的声誉不够高。我已经编辑了这个问题,包括显示位图的代码,谢谢你的回答。我已经修改了我的函数以使用您建议的结构。我正在使用:“BGRA COLOR\u table\u offset;颜色表偏移量=(BGRA)bmp\U数据+(sizeof(mBitmapFileHdr\U t)+sizeof(mBitmapInfoHdr\U t));'但是显示的位图显示了错误的颜色。我还缺什么吗?嗨,不客气。很难说问题出在哪里。你能把预期和观察到的结果上传到某个地方并留下链接吗?我猜,因为我看到提到了
CBitmapViewerDlg
,您正在windows下使用MFC应用程序,因此文件中的第一个字节是多字节字中最低有效字节。即:
FF 00 00
(BGRA)文件中的字节将作为
00 00 FF
(ARGB)加载到寄存器中,最低有效字节将是蓝色通道,而不是alpha通道。这可能很简单,就像颠倒BGRA结构中字段的顺序一样。Andrew,这似乎就是我所认为的——rgb通道的反转。我已经更新了答案,添加了显示位图的代码。再次感谢您的帮助。我已经实现了你的功能,它工作得很好。我有艾迪
uint32_t header_size = (bmp_data[14] & 0xff) | ((bmp_data[15] & 0xff) << 8) | ((bmp_data[16] & 0xff) << 16) | ((bmp_data[17] & 0xff) << 24);
BGRA *colour_table_offset = (BGRA *)(bmp_data + 14 + header_size);