我很欣赏这可能非常简单,但经过花费大量时间研究后,我很难想出一个明确的答案。
我正在编写一些代码来打开并在带有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;
}
安德鲁
答案 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
'预期'结果:(我知道它会颠倒并让渠道混乱)
实际结果:
如果我们然后更改内循环的代码来交换通道,我们得到预期的结果(一个仍然颠倒的 - 我没有费心考虑自上而下或自下而上的位图为此例子)
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) );