关于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
关于BMP的更多相关文章
- Android raw to bmp
Android raw 格式转 bmp 图像 raw 保存的为裸数据,转换时都需要把它转成RGBA 的方式来显示.其中: 8位RAW: 四位RGBA 来表示一位灰度; 24位RAW: 三位RGB相同, ...
- 你所能用到的BMP格式介绍
原理篇: 一.编码的意义. 让我们从一个简单的问题开始,-2&-255(中间的操作符表示and的意思)的结果是多少,这个很简单的问题,但是能够写出解答过程的人并不 多.这个看起来和图片格式没有 ...
- Linux C语言解析并显示.bmp格式图片
/************************* *bmp.h文件 *************************/ #ifndef __BMP_H__ #define __BMP_H__ # ...
- 从Java String实例来理解ANSI、Unicode、BMP、UTF等编码概念
转(http://www.codeceo.com/article/java-string-ansi-unicode-bmp-utf.html#0-tsina-1-10971-397232819ff9a ...
- Linux C语言解析.bmp格式图片并显示汉字
bmp.h 文件 #ifndef __BMP_H__ #define __BMP_H__ #include <unistd.h> #include <stdio.h> #inc ...
- BMP图像差分/比较
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char ...
- Matlab 读取文件夹中所有的bmp文件
将srcimg文件下的bmp文件转为jpg图像,存放在dstimg文件夹下 str = 'srcimg'; dst = 'dstimg'; file=dir([str,'\*.bmp']); :len ...
- 提取bmp图片的颜色信息,可直接framebuffer显示(c版本与python版本)
稍微了解了下linux的framebuffer,这是一种很简单的显示接口,直接写入像素信息即可 配置好的内核,会有/dev/fbn 的接口,于是想能否提前生成一个文件,比如logo.fb,里面仅包含像 ...
- 【VC++技术杂谈006】截取电脑桌面并将其保存为bmp图片
本文主要介绍如何截取电脑桌面并将其保存为bmp图片. 1. Bmp图像文件组成 Bmp是Windows操作系统中的标准图像文件格式. Bmp图像文件由四部分组成: (1)位图头文件数据结构,包含Bmp ...
- gif jpg bmp png的区别
PNG格式图片因其高保真性.透明性及文件大小较小等特性,被广泛应用于网页设计.平面设计中.网络通讯中因受带宽制约,在保证图片清晰.逼真的前提下,网页中不可能大范围的使用文件较大的bmp.jpg格式文件 ...
随机推荐
- 初识跨终端Web
近期试读了<跨终端Web>这本书的部分章节,既为了拿到书,也为了记录下读后的收获的东西,这会是个非常好的习惯吧. 标题为"初识跨终端Web".对我来说最贴切了,在此之前 ...
- 用Java编写的http下载工具类,包含下载进度回调
HttpDownloader.java package com.buyishi; import java.io.FileOutputStream; import java.io.IOException ...
- mysql优化-----多列索引的左前缀规则
索引优化策略 :索引类型 .1B-tree索引 关注的是:Btree索引的左前缀匹配规则,索引在排序和分组上发挥的作用. 注:名叫btree索引,大的方面看都用的二叉树.平衡树.但具体的实现上,各引擎 ...
- linux命令启动服务(tomcat服务或者jar包)
启动tomcat: 1.方式一:直接启动 ./startup.sh 2.方式二:nohup ./startup.sh & 启动后,关闭当前客户端连接,重新启动一个查看是 否已经启动 启动jar ...
- centreon问题总结
1.SNMP TABLE ERROR : Requested table is empty or does not exist 这是SNMP的服务端查询客服端失败,失败的原理是权限不足 解决办法: v ...
- CentOS/Ubuntu安装GLIBCXX3.4.21
经过测试“GLIBCXX3.4.21 not find”这篇博文解决了我的问题. 以下是安装步骤: 一.首先查看当前gcc版本 strings /usr/lib/x86_64_linux-gun/ ...
- html5--6-5 CSS选择器2
html5--6-5 CSS选择器2 实例 学习要点 掌握常用的CSS选择器 了解不太常用的CSS选择器 什么是选择器 当我们定义一条样式时候,这条样式会作用于网页当中的某些元素,所谓选择器就是样式作 ...
- Oracle:热备测试
我们知道Oracle数据库热备有3步: 1. alter tablespace tbname begin backup: 2. cp /××× to /×× 3. alter ta ...
- Viewpager animation duration setting
private void animatePagerTransition(final boolean forward) { ValueAnimator animator = ValueAnimator. ...
- java前端学习步骤
前端说的比较好的知乎:https://www.zhihu.com/question/22759296 网站开发绝杀技:https://ke.qq.com/course/20945?from=qqcha ...