关于BMP

关于BMP位图的资料网上有很多,内容也比较基础。本文实现BMP位图的读取、显示、保存,并对一些重要的问题进行说明(包括字节对齐、内存中的存储顺序、调色板)。

BMP文件共包括文件头、信息头、调色板(位深<=8的图像含有此项)、位图数据四大部分:

各部分的具体说明可以参考[1]

下面是位图的读取、显示、保存实现的主体代码。(完整工程下载:Bmptest

CString Filename;
CStatic Picture;
long Width ;
long Height ;
unsigned short BitCount;
long Stride;
unsigned char* Img;
void OpenBmp();
void SaveBmp();

//打开位图
void CBmptestDlg::OpenBmp()
{
CRect zcRect;
Picture.GetClientRect(&zcRect);
CDC* pDC=Picture.GetDC();


BITMAPFILEHEADER header;//文件头
BITMAPINFOHEADER infoheader;//信息头
BITMAPINFO *bitmapinfo=NULL;


FILE *fp=fopen(Filename,"rb");
if(fp==NULL){return;}
fread(&header,sizeof(BITMAPFILEHEADER),1,fp);
if(header.bfType != 0x4D42){return;}
fread(&infoheader,sizeof(BITMAPINFOHEADER),1,fp);
Width = infoheader.biWidth;
Height = infoheader.biHeight>0?infoheader.biHeight:-infoheader.biHeight;
BitCount = infoheader.biBitCount;
Stride=((Width*BitCount+31)>>5)<<2;//一行字节数,4字节对齐
int Imgsize=Stride*Height;
if (Img!=NULL){free(Img);}
Img=(unsigned char*)malloc(Imgsize);
if(BitCount<=8)
{
int Palettesize=header.bfOffBits-sizeof(BITMAPFILEHEADER)-sizeof(BITMAPINFOHEADER);//不能PaletteLen=1<<biBitCount,因调色板大小可在[2,256]取值
RGBQUAD *Palette=(RGBQUAD*)malloc(Palettesize);
fread(Palette,Palettesize,1,fp);
bitmapinfo=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER)+Palettesize);
bitmapinfo->bmiHeader=infoheader;
memcpy(bitmapinfo->bmiColors,Palette,Palettesize);
fread(Img,Imgsize,1,fp);
free(Palette);
}

if(BitCount>=16)
{
bitmapinfo=(BITMAPINFO*)malloc(sizeof(BITMAPINFOHEADER));
bitmapinfo->bmiHeader=infoheader;
fread(Img,Imgsize,1,fp);
}


SetStretchBltMode(pDC->m_hDC,COLORONCOLOR);
StretchDIBits(pDC->m_hDC,0,0,zcRect.Width(),zcRect.Height(),0,0,Width,Height,Img,bitmapinfo,DIB_RGB_COLORS,SRCCOPY);
ReleaseDC(pDC);
free(bitmapinfo);
fclose(fp);
return;
}


//保存位图
//常见需求是由位图数据、宽、高、位深将其保存为位图,故此函数只考虑8位灰度,16\24\32彩色位图
void CBmptestDlg::SaveBmp()
{
if(BitCount<8)return;
if(Img==NULL)return;
FILE*fp=fopen(Filename,"wb");
if(fp==NULL)return;
BITMAPFILEHEADER hearder;
BITMAPINFOHEADER infohearder;
int ImgSize=Stride*Height;
if (BitCount==8)
{
int PaletteSize=sizeof(RGBQUAD)*256;
hearder.bfType=0X4D42;
hearder.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PaletteSize+ImgSize;//文件总大小
hearder.bfReserved1=0;
hearder.bfReserved2=0;
hearder.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+PaletteSize;
fwrite(&hearder,sizeof(BITMAPFILEHEADER),1,fp);


infohearder.biSize=sizeof(BITMAPINFOHEADER);
infohearder.biWidth=Width;
infohearder.biHeight=Height;//倒序
//infohearder.biHeight=-Height;//顺序
infohearder.biPlanes=1;
infohearder.biBitCount=BitCount;
infohearder.biCompression=BI_RGB;
infohearder.biSizeImage=ImgSize;
infohearder.biXPelsPerMeter = 0;
infohearder.biYPelsPerMeter = 0;
infohearder.biClrUsed = 0;
infohearder.biClrImportant = 0;
fwrite(&infohearder,sizeof(BITMAPINFOHEADER),1,fp);


RGBQUAD * palette=(RGBQUAD*)malloc(PaletteSize);
for (int i=0;i<256;i++) //这里针对8位灰度图
{
palette[i].rgbRed=palette[i].rgbGreen=palette[i].rgbBlue=i;
palette[i].rgbReserved=0;
}
fwrite(palette,PaletteSize,1,fp);
fwrite(Img,ImgSize,1,fp);
}


if(BitCount>=16)
{
hearder.bfType=0X4D42;
hearder.bfSize=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+ImgSize;//文件总大小
hearder.bfReserved1=0;
hearder.bfReserved2=0;
hearder.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
fwrite(&hearder,sizeof(BITMAPFILEHEADER),1,fp);


infohearder.biSize=sizeof(BITMAPINFOHEADER);
infohearder.biWidth=Width;
infohearder.biHeight=Height;//倒序
//infohearder.biHeight=-Height;//顺序
infohearder.biPlanes=1;
infohearder.biBitCount=BitCount;
infohearder.biCompression=BI_RGB;
infohearder.biSizeImage=ImgSize;
infohearder.biXPelsPerMeter = 0;
infohearder.biYPelsPerMeter = 0;
infohearder.biClrUsed = 0;
infohearder.biClrImportant = 0;
fwrite(&infohearder,sizeof(BITMAPINFOHEADER),1,fp);
fwrite(Img,ImgSize,1,fp);
}
fclose(fp);
}

 

界面:

关于四字节对齐

Windows为了存取的效率,规定位图存储时必须满足一行为4字节的整数倍。我们处理位图时通常需要求取一行对应的字节数,需要使用如下公式:

另一个常用的公式但并不适用于位深为1、4的位图:

关于图像存储顺序:

位图信息头BITMAPINFOHEADER中的biHeight不仅体现位图高度,还标记此位图的存储方式。对于一幅位图:

若biHeight>0,则内存中存储顺序如下,在该模式下,内存中第一字节实际对应位图的左下角,大部分位图都按此方式存储。

若biHeight<0,则内存中存储顺序如下,内存第一字节对应位图左上角。

此类情况模式常用的场合是:用户自己产生一幅图像,譬如在数据采集系统中常常需要将数据进行图像显示。这时用户需要开辟一块内存并按一定方式填充该内存。由于顺序存储方式对用户来说更加直观,操作更加方便,所有常使用第二种模式。这时,在显示和保存位图时将BITMAPINFOHEADER中的biHeight设为负数即可。

关于索引图像

位深<=8位的位图才有调色板。本在编程时犯过一个错误,就是误认为8位深度的索引图调色板中就含有256(即2^8)种颜色,在读取调色板数据时若按此长度读会导致最终显示图像时发生偏移。后发现调色板颜色数可以是[2,256]范围内的任何值。

如在PS中(图像—》模式—》索引颜色)可以设置索引颜色数20:

实际经测试可以发现调色板中包含21项:

0:( 20, 50, 26)

1:( 45, 77, 44)

2:( 9, 19,  8)

3:( 12, 40, 8)

4:( 19, 61, 8)

5:( 72,107, 61)

6:( 37, 82, 20)

7:( 60,110, 32)

8:(104,127, 88)

9:( 90,139, 51)

10:(135,160, 86)

11:( 45, 48, 18)

12:(174,171,134)

13:( 85, 68, 39)

14:(245,195,163)

15:(246,127, 75)

16:(129, 77, 56)

17:(241, 62, 22)

18:(125, 28, 11)

19:(173, 20,  9)

20:( 0,  0,  0)

另外一点,调色板类型RGBQUAD定义为:

typedef structtagRGBQUAD {

BYTE    rgbBlue;

BYTE    rgbGreen;

BYTE    rgbRed;

BYTE    rgbReserved;

} RGBQUAD;

即每一项四字节表示,每一字节分别表示R、G、B、A分量。这一点是重要的,也就是说在自定义调色板数据时,不管位深是1、4、8,调色板中每一分量都是在[0,255]间变化,而与位深大小无关。关于索引图像有个帖子可以参考一下[2]

参考:

[1]http://www.cnblogs.com/xiehy/archive/2011/06/07/2074405.html

[2]http://bbs.csdn.net/topics/110048102

时间: 2024-11-04 14:12:27

关于BMP的相关文章

VC中显示ICON和BMP图片

显示ICON图标,如下: static HICON hIcon  = (HICON)::LoadImage(       AfxGetInstanceHandle(),        TEXT("1.ico"),       IMAGE_ICON,       0, 0,       LR_DEFAULTCOLOR | LR_CREATEDIBSECTION | LR_LOADFROMFILE);          CClientDC dc(this);       dc.DrawIc

Linux framebuffer显示bmp图片

framebuffer简介     帧缓冲(framebuffer)是Linux为显示设备提供的一个接口,把显存抽象后的一种设备,他允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作.framebuffer是LCD对应的一中HAL(硬件抽象层),提供抽象的,统一的接口操作,用户不必关心硬件层是怎么实施的.这些都是由Framebuffer设备驱动来完成的.     帧缓冲设备对应的设备文件为/dev/fb*,如果系统有多个显示卡,Linux下还可支持多个帧缓冲设备,最多可达32个,分别为/d

生成一个空白BMP的简单代码【转】

转自:http://blog.chinaunix.net/uid-15063109-id-4275395.html 做图像处理时,有时需要临时生成图使用.以下是生成320x240 24位图的一个简单的代码实现: #define WIDTHBYTES(bits) ((DWORD)(((bits)+31) & (~31)) / 8) void makebmp() { int nSize =abs(long(240 * WIDTHBYTES(24 * 320))); char* buff = new

BMP Header Structure

1 /* ***** BEGIN LICENSE BLOCK ***** 2 * 3 * $Id: bitmap.h,v 1.3 2004/06/30 16:44:52 asuraparaju Exp $ $Name: Dirac_1_0_2 $ 4 * 5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 6 * 7 * The contents of this file are subject to the Mozilla Public License 8 * Vers

图像处理笔记一bmp文件结构处理与显示

1.1图和调色板的概念 如今Windows(3.x以及95,98,NT)系列已经成为绝大多数用户使用的操作系统,它比DOS成功的一个重要因素是它可视化的漂亮界面.那么Windows是如何显示图象的呢?这就要谈到位图(bitmap). 我们知道,普通的显示器屏幕是由许许多多点构成的,我们称之为象素.显示时采用扫描的方法:电子枪每次从左到右扫描一行,为每个象素着色,然后从上到下这样扫描若干行,就扫过了一屏.为了防止闪烁,每秒要重复上述过程几十次.例如我们常说的屏幕分辨率为640×480,刷新频率为7

关于EJB--实体Bean的BMP和CMP选择

EJB有两种主要类型BMP(Bean managed persistence )和CMP(Container managed persistence ),这两种类型各有优缺点. BMP是在Bean中完成对数据库JDBC的各种调用,也就是说,在你的实体bean(entity bean)中,明确写入了SQL语句,如"insert .. "或"select ..",并且使用Datasource获得一个数据库资源以及连接(connection)从而对数据库直接进行增加删除修

16位bmp文件中RGB555转RGB565算法

做tft彩屏显示图片的时候,显示16位位图,显示屏的显示模式为RGB565.使用img2lcd转换后的16位bmp,显示出来后,颜色有偏差:转换为565格式的bin文件,显示完全正常,可以确定转换为bmp后,格式为RGB555.网上查找相关资料显示,Windows 图片查看器显示正常的图片,均为RGB555格式,系统自带画图画图工具保存时,不支持将文件保存为16位位图格式. 以下为555转565的程序片段,转换后的结果和使用img2lcd软件保存为565格式的结果完全相同.转换的结果其实就是将原

C++读取、旋转和保存bmp图像文件编程实现

以前也遇到过bmp文件的读写.这篇博客很好,写的其他内容也值得学习. 参考:http://blog.csdn.net/xiajun07061225/article/details/6633938  学习

BMP结构详解

位图BITMAPINFOHEADER 与BITMAPFILEHEADER: 先来看BITMAPINFOHEADER,只写几个主要的biSize包含的是这个结构体的大小(包括颜色表)    biWidth和biHeight分别是图片的长宽    biPlanes是目标绘图设备包含的层数,必须设置为1    biBitCount是图像的位数,例如24位,8位等    biXPelsPerMeter, biYPelsPerMeter 是现实世界中每米包含的像素数 设为3780即可    biSizeI

读写bmp图片

1 //读写bmp图像,好久的前写的,放这做记录吧 2 3 4 //h 5 #pragma once 6 #include <string> 7 using std::string; 8 9 class BmpRW 10 { 11 public: 12 BmpRW(void); 13 ~BmpRW(void); 14 15 public: 16 char* R8Bitmap(char *imName, int &imWidth, int &imHeight); 17 bool