位图文件包括:位图文件头BITMAPFILEHEADER;
位图信息BITMAPINFO(位图信息头BITMAPINFOHEADER、颜色表RGBQUAD);
位图数据。
位图文件头主要用于识别位图文件,位图文件头结构的定义为:
其大小为:2+4+2+2+4 = 14byte
其中的bfType值是“BM”(0x4d42)转化为十进制为19778,标志该文件是位图文件。
bfSize的值是位图文件的大小,等于 bfOffBits + DibWidth(四字节对齐后的宽度) * Height
bfOffBits表示像素数据距离文件起始位置的偏移量。
我们拿一个灰度图片举个例子,其每行像素个数为511,高度是512。
bfSize = 1078 + 512(511四字节对齐后就是512)*512 = 263222(byte)。
bfOffBits = 14 + 40(sizeof(BITMAPINFOHEADER)) + 256*4(颜色表大小) = 1078 。
那么彩色图片的信息我们也看下,其每行像素个数为511,高度是512:
bfSize = 54 + 512(对齐后的啊)*512 * 3 = 786486(byte)
bfOffBits = 14 + 40 = 54(彩色图片一个像素用24位表示,故没有颜色表)
问题1:有关四字节问题:
就是为了让每一行的像素的个数都保证是4的个数,当然对齐后肯定是有优点的,优点是对于计算机存储来说的。
四字节对齐目的是让原本不是4的倍数的每行像素变成4的倍数,如上面的511->512
那么是怎么变得?我们首先要知道原本图片中一行像素个数m_nWidth,然后还有知道一个像素占了几位(1byte = 8bit),然后对齐后的宽度m_nDibWidth 为:(注意这里的数据全是整数运算,也就是说小数后面全舍掉后再参与运算)
m_nDibWidth = (m_nWidth*m_nBitCount + 31) / 32 * 4;
512 = (511 * 8 + 31)/32*4 = 4119/32*4 = 128*4=512
问题2:颜色表
说道颜色表,就必须知道RGB是怎么映射出来的?
也就是说,由于我们灰度图片是8bit的,也就是有256个灰度级,如果要表示一个灰度级的像素,我需要让R分量,G分量,B分量都是一样的数,像素少是可以这么做,众所周知,现在手机图片都做到1亿了,那一张图片这样表示显然浪费?于是有了颜色表的存在。
颜色表就是将256个灰度级用256*4的数组先定义好,然后图片的像素进行映射。那么那个数组长啥样?
从这个图中我们能知道:像素点1的灰度级为2,像素点2的灰度级为n(0=<n<=255)
实际中用数组定义的样子是什么样的:
bmiColors是一个4*256 = 1024大小的数组,上面显示的是bmiColors[0] ,显然bmiColors[1]肯定是:
包含两部分:位图信息头BITMAPINFOHEADER、颜色表RGBQUAD,其中颜色表不一定都有。
位图信息头的结构体定义如下:
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
每个参数的详细介绍:
biBitCount:对于每个像素的字节数,可能有以下取值:
1,单色图,调色板中含有两种颜色,也就是我们通常说的黑白图片
4,16色图
8,256色图,通常说的灰度图,但也可以有颜色,取决于使用何种颜色表
16,64K图,一般没有调色板,图像数据中每两个字节表示一个像素,5个或6个位表示一个RGB分量
24,16M真彩色图,一般没有调色板,图像数据中每3个字节表示一个像素,每个字节表示一个RGB分量
32,4G真彩色,一般没有调色板,每4个字节表示一个像素,相对24位真彩图而言,加入了一个透明度,即RGBA模式
位图数据是像素的信息,其大小为:nDL = m_nDibWidth * m_nHeight,单位:字节
而m_nDibWidth又和biBitCount以及图片宽度有关。