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

时间:2014-07-31 22:32:57

标签: c++

我很欣赏这可能非常简单,但经过花费大量时间研究后,我很难想出一个明确的答案。

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

颜色表应该位于文件中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++;
            }           

        }
    }
}

下面显示位图的修改功能:

int CBitmapViewerDlg::display_bmp(char *bmp_data, unsigned short left, unsigned short top, CDC *dc)

{     无符号短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;

}

安德鲁

2 个答案:

答案 0 :(得分:4)

位图文件头总是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);

这提出了另一点,您使用unsigned short表示32位数量。那是错的。

调色板的偏移量将紧接在位图信息标题之后。如果压缩类型为BI_BITFIELDS,还有可能在颜色表之前有3个4字节掩码,但我从未见过使用过的压缩类型。如果压缩类型不是BI_RGB,则可以轻松地拒绝阅读文件。

BGRA *colour_table_offset = (BGRA *)(bmp_data + 14 + header_size);

答案 1 :(得分:1)

公平地说,BMP格式的文档可能会有点混乱。多年来的各种新增/定制肯定没有帮助这种情况,imho。

简短的回答是颜色表或调色板的偏移量为54.假设在Windows下使用Gimp保存图像 - 这对于今天玩过的4,8和24位图像都是如此。

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

  
    

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

  

好吧,如果您查看BMP的格式规范,例如http://en.wikipedia.org/wiki/BMP_file_format(请参阅Windows BITMAPINFOHEADER的表格)并查看此字段的说明,您可以看到对于带有颜色表的图像,它是 (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;
}

应用于源

中显示的2x2.bmp时的输出
Width: 2
Height: 2
Planes: 1
Bits/Pixel: 4
Colors used: 4
Important Colors: 4
Offset of pallete: 54
Offset of colour data: 70

应用于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

应用于上述bmp的8位版本时的输出

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

在这里,我只是假设RGB值,但是我接收了它们。你可以清楚地看到错误的结果。正如我在评论中提到的那样,这是颠倒bgra结构中通道顺序的问题。图像颠倒的事实完全是另一回事,我会让你去研究那个。 :P

'预期'结果:(我知道它会颠倒并让渠道混乱) enter image description here

实际结果:

enter image description here

如果我们然后更改内循环的代码来交换通道,我们得到预期的结果(一个仍然颠倒的 - 我没有费心考虑自上而下或自下而上的位图为此例子)

            uint32_t palIndex;
            palIndex = curRow[x];
            BGRA curCol = colour_table_offset[palIndex];
//            SetPixel(hdc, x, y, curCol);

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

enter image description here