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