Andrew Huang 转载请注明作者及网址
位图(bmp)是所有图像格式最接近RGB的格式,因此它的处理最为简单。所以一般用来做RGB编程样例,但是在BMP虽简单,但是也充满不少细节,因此刚开处理时碰到不少问题.
BMP格式是由MS和IBM共同发起的格式,用于MS的操作系统和IBM OS/2上,但是用得较少.因此成BMP几乎成了MS的单独使用标准格式.在其的操作系统都使用这种格式,其他图像实际最后都要转换成BMP显示.
1.BMP文件格式分析
1.1 BMP文件分区
如果图像是小于等于8bpp格式的(包括1bpp,2bpp,4bpp,8bpp几种).BMP文件分为四大块
如果是16bpp/18bpp/24bpp/32bpp的位图,本身格式只分为三大块
1.2.BMP文件头和信息头定义
.BMP有如下定义.
这里注意几个细节.
1.文件头的定义不是字节对齐的,因此用结构去读位图文件头时,必须取消字节对齐
2.里面所有整数,int32(int )或int16 (short)都是采用小端字节序的,因此用结构读取整数时,必须考虑字节序转换.
3.对于显示位图最重要几个数据是
bfOffBits :真正RGB数据区相于文件头的的偏移量
Width: 图像宽度
Height: 图像高度
biBitCont: BPP的值.
1.3 RGB数据区的排列
BMP的RGB数据区是排列非常奇怪,在最开始编程时忽略这一点,导致的显示图像总不正常.
1.首先每一个点的数据是占用三个字节,并且是按 B,G,R的顺序倒排.
2.另外整个数据区也是按行倒排的,即文件数据区中第一行存的是最后一行的数据!,而文件最后一行的数据才是第一行数据. 这个数据区第一个点实际上是左下角的点.而最后一个点实际上
1.4 调色板数据处理
2.Frame Buffer显示位图
首先们处理最简单的情况,即当前FB的bpp和高度和宽度正好与当前位图图像一致.主要来看显示算法.
2.1 文件头/信息头的读取.
对于BMP信息的读取.有两种方法,一种是定两个结构把文件头和信息头读出来,这一种必须考虑字节序和字节对齐,但代码可读性强.
另外一种是把四个最关键的信息直接用偏移量来读取,这种无需考虑字节序和字节对齐,但是代码可读性差.关键是看编程如何处理.
2.1.1 按结构来读数据
这里定义了BmpFileHeader和BmpInfoHeader,其中用了#pragma pack(1) 表示取消字节对齐,在大部分编译器下,包括GCC都可以使用.
#pragma pack(1) struct _BmpFileHeader { unsigned short bfType; //Specifies the file type. It must be BM
unsigned long bfSize; //Specifies the size, in bytes, of the bitmap file
unsigned long bfReserved1; //Reserved; must be zero
unsigned long bfRebfOffBits; //数据区偏移量 Specifies the offset, in bytes, from the BITMAPFILEHEADER structure to the bitmap bits
};
typedef struct _BmpFileHeader BmpFileHeader; //BMP文件头
struct _BmpInfoHeader { unsigned long biSize; //Info Header的大小,Specifies the number of bytes required by the structure.
long biWidth; //biWidth Specifies the width of the bitmap.单位是像素
long biHeight; //Specifies the height of the bitmap, in pixels.
unsigned short biPlanes; //Specifies the number of planes for the target device. This value must be set to 1.
unsigned short biBitCount ; //BPP值,Specifies the number of bits per pixel,such as 1,4,8,24 .
unsigned long biCompression;//Specifies the type of compression for a compressed bottom-up bitmap
//(top-down DIBs cannot be compressed). This member can be one of the
//following values: BI_RGB,BI_RLE8,BI_RLE4,BI_BITFIELDS,BI_JPEG.
unsigned long biSizeImage; //图像尺寸,Specifies the size, in bytes, of the image. This may be set to zero for BI_RGB bitmaps.
long biXPelsPerMeter;//Specifies the horizontal resolution, in pixels per meter, of the target device for the
//bitmap. An application can use this value to select a bitmap from a resource
//group that best matches the characteristics of the current device.
long biYPelsPerMeter;//Specifies the vertical resolution, in pixels per meter, of the target device for the bitmap.
unsigned long biClrUsed; //颜色数,Specifies the number of color indexes in the color table that are actually used by the bitmap.
//If this value is zero, the bitmap uses the maximum number of colors corresponding
//to the value of the biBitCount member for the compression mode specified by biCompression.
unsigned long biClrImportant; //Specifies the number of color indexes that are required for displaying the bitmap. If this value is zero,
//all colors are required.
};
typedef struct _BmpInfoHeader BmpInfoHeader; //BMP文件信息
struct _RGBQuad { unsigned char rgbBlue; //Specifies the intensity of blue in the color.
unsigned char rgbGreen; //Specifies the intensity of green in the color.
unsigned char rgbRed; //Specifies the intensity of red in the color.
unsigned char rgbReserved; //Reserved; must be zero.
};
typedef struct _RGBQuad RGBQuad; //RGB调色板数据
#pragma pack()
|
如果是小端字节序(X86,ARM),用这个结构直接读取文件即可,而大端字节序的CPU必须在读取后,每个成员要做一下字节序转换,即四字节对调(1,4字节对调,2,3字节对调)
结构读取算法
BmpFileHeader header; BmpInfoHeader info; //读文件头信息
len = fread(&header,1,sizeof(header),file); if(len != sizeof(header)) { printf("bmp file %s header failure\n",file_name); fclose(file); return -2; } if(header.bfType!=DIB_HEADER_MARKER) { printf("bmp file %s no BM \n",file_name); fclose(file); return -3; } //读文件信息
len = fread(&info,1,sizeof(info),file); if(len != sizeof(info)) { printf("bmp file %s info failure\n",file_name); fclose(file); return -4; }
|
2.1.2 按位移来读取数据
因为显示只需要四个数据即可,因此直接定义这个变量偏移量,然后用lseek/fseek去移到指定位置读取即可.
2.2 RGB数据读取.
因为是倒排的数据,如果需要把BMP的数据映射到显存上.则需要一些特殊处理.因为RGB的显存可以自由移动可以在倒显存显示即可.
首先读文件点数据时,先移到显存最后一行开始处理,然后不断在显存上向前移动指针。
#define RGB_24_888(r,g,b) ( ((r & 0xff)<<16) | ((g & 0xff ) << 8) | ((b & 0xff) << 0) )
//注意BMP的RGB数据区是倒排,即图像最后一行存在文件最开始,这样文件中的第一点实际上是左下角数据.
//最后一个点是右上角数据。
int bmp_show_le_24bpp(char * file_name,unsigned char * fb_buf,int buf_len,int width,int height) { FILE * file = NULL; int len,i,j,cnt=0; unsigned char ary[3]; //BMP的24位是用3byte保存
unsigned char * p; unsigned long value; BmpFileHeader header; BmpInfoHeader info;
printf("show BMP file %s,width %d,height %d\n",file_name,width,height); file = fopen(file_name,"rb");
if(file == NULL) { printf("open bmp file %s failure\n",file_name); return -1; }
len = fread(&header,1,sizeof(header),file); if(len != sizeof(header)) { printf("bmp file %s header failure\n",file_name); fclose(file); return -2; }
if(header.bfType!=DIB_HEADER_MARKER) { printf("bmp file %s no BM \n",file_name); fclose(file); return -3; }
len = fread(&info,1,sizeof(info),file); if(len != sizeof(info)) { printf("bmp file %s info failure\n",file_name); fclose(file); return -4; }
p = (fb_buf+width*(height-1)*4); //移到FrameBuffer最后一行
printf("bmp %s: fb_buf %x,Height %d,Width %d,bpp %d,data offset %d\n",file_name,fb_buf,info.biHeight,info.biWidth,info.biBitCount,header.bfRebfOffBits);
//移到数据区
fseek(file,header.bfRebfOffBits,SEEK_SET);
//这里假设图像尺寸与屏幕完全一致.
for(i=0; i< height ; i++) { //printf("%s:fb buf %x,line[%d]=%x\n",__FUNCTION__,fb_buf,i,p);
for(j=0;j< width ; j++) { if(fread(ary,1,sizeof(ary),file)<0) break; //注意BMP文件中,是按BGR的顺序保存数据
value = RGB_24_888(ary[2],ary[1],ary[0]);//注意其倒排顺序 *((unsigned long *)p) = value; p+=4; //if(i<10)
// printf("point[%d],value %x, red %x,green %x,blue %x\n",i,value,ary[2],ary[1],ary[0]); } p-= width*4*2; //回退2行,因为内循环移动一行,还要上移一行 }
fclose(file); }
|
附录: frame Buffer编程系列
1. 设备操作
2. 对显存操作
3. 位图显示
4. png/jpg/gif 显示
4.fbv 分析
5.截图程序
阅读(1040) | 评论(0) | 转发(0) |