今天研究了一下位图的加载,以BMP为例,本来在WIN32下,可以直接LoadImage,然而这个函数太通用了,以至于对于图像的很多东西都不很清楚,具体对图像操作是很麻烦。
对于一个设备无关的位图(DIB),主要由4部分组成
1文件头
2信息头
3RGB色彩表(不一定有)
4位图像素位
对于文件头,其定义如下,参照MSDN
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType; //signature word “BM” or 0X4D42
DWORD bfSize; //entire size of file
WORD bfReserved1; //must be zero
WORD bfReserved2; //must be zero
DWORD bfOffBits; //offset in file of DIB pixel bits
} BITMAPFILEHEADER;
对于Windows DIB,紧跟在文件头后的信息头定义如下
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth; //size of the structure
LONG biHeight; //width of the image in pixels
WORD biPlanes; //height of the image in pixels
WORD biBitCount // = 1
DWORD biCompression; //bits per pixel
DWORD biSizeImage; //compression code
LONG biXPelsPerMeter; //number of bytes in image
LONG biYPelsPerMeter; //horizontal resolution
DWORD biClrUsed; //number of colors used
DWORD biClrImportant; //number of important colors
} BITMAPINFOHEADER;
对于色彩表,其定义如下
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue; //blue level
BYTE rgbGreen; //green level
BYTE rgbRed; //red level
BYTE rgbReserved; // = 0
} RGBQUAD;
而对于像素位,是由从图像的底行开始,并沿着图像向上增长的水平行组织的
了解了DIB的基本结构后,对DIB的操作就可以自己写出来了,我写了一个
CPicture类,进行对DIB的操作,首先是产生DIB
BOOL CPicture::Create(int _width, int _height, int _depth)
{
width = _width;
height = _height;
depth = _depth;
bytes_per_line = ScanBytes(width, depth);
bytes_per_pixel = PixelBytes(depth);
long bitsAlloc = bytes_per_line * height;
long headersize = sizeof(BITMAPINFOHEADER);
if(depth == 8)
headersize += sizeof(RGBQUAD) * 256;
if((hGlobal = ::GlobalAlloc(GMEM_MOVEABLE, bitsAlloc + headersize)) == 0)
return FALSE;
Info = (BITMAPINFO *)::GlobalLock(hGlobal);
Bits = (void*)((long*)Info + headersize);
Info->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
Info->bmiHeader.biWidth = width;
Info->bmiHeader.biHeight = height;
Info->bmiHeader.biBitCount = (unsigned short)depth;
Info->bmiHeader.biPlanes = 1;
Info->bmiHeader.biXPelsPerMeter = 0;
Info->bmiHeader.biYPelsPerMeter = 0;
Info->bmiHeader.biClrUsed = 0;
Info->bmiHeader.biClrImportant = 0;
Info->bmiHeader.biCompression = BI_RGB;
Info->bmiHeader.biSizeImage = bitsAlloc;
return TRUE;
}
然后就是读入BMP文件
BOOL CPicture::LoadPicture(CFile &file, int ox, int oy)
{
BITMAPFILEHEADER header;
BITMAPINFOHEADER infoheader;
if(file.Read(&header, sizeof(header)) != sizeof(header)
|| header.bfType != (WORD)(('M' << 8) | 'B')
|| file.Read(&infoheader, sizeof(infoheader)) != sizeof(infoheader)
|| infoheader.biSize < sizeof(BITMAPINFOHEADER)
|| infoheader.biCompression != BI_RGB)
return FALSE;
int _width = infoheader.biWidth;
int _height = infoheader.biHeight;
int _depth = infoheader.biBitCount;
if(!Create(_width + ox, _height + oy, _depth))
return FALSE;
if(_width + ox > Width() || _height + oy > Height() || _depth != Depth())
return FALSE;
int bits_off = sizeof(BITMAPFILEHEADER) + infoheader.biSize;
if(_depth == 8)
{
int csize = sizeof(RGBQUAD) * 256;
if(file.Read(Info->bmiColors, csize) != csize)
return FALSE;
bits_off += csize;
}
file.Seek(bits_off, CFile::begin);
int length = _width * BytesPerPixel();
int filler = ScanBytes(_width, _depth) - length;
for(int y = _height - 1; y >= 0; y--)
{
if(file.Read(GetBits(ox, oy + y), length) != length)
return FALSE;
if(filler)
file.Seek(filler, CFile::current);
}
// file.Read(GetBits(), infoheader.biSizeImage);
return TRUE;
}
另外还写了图像的复制与重叠函数:
//图像复制
void CPicture::Copy(const CPicture *picture, RECT &rect)
{
int len = (rect.right - rect.left) * 3;
for(int y = rect.top; y < rect.bottom; y++)
{
memcpy(GetBits(rect.left, y), picture->GetBits(rect.left, y), len);
}
}
//图像重叠
void CPicture::MixPicture(const CPicture *picture, RECT &rect, COLORREF trans_color)
{
const unsigned char trans_b = GetBValue(trans_color);
const unsigned char trans_g = GetGValue(trans_color);
const unsigned char trans_r = GetRValue(trans_color);
for(int y = rect.top; y < rect.bottom; y++)
{
char *p = (char *)GetBits(rect.left, y);
const BYTE * q = (BYTE *)picture->GetBits(rect.left, y);
for(int x = rect.left; x < rect.right; x++)
{
const BYTE b = *q++;
const BYTE g = *q++;
const BYTE r = *q++;
if(b != trans_b || g != trans_g || r != trans_r)
{
p[0] = b;
p[1] = g;
p[2] = r;
}
p += 3;
}
}
}
然后又写了一个CPictureDraw类,专门用来加载并显示24位的位图
//创建显示用图像,选用24位位图
BOOL CPictureDraw::Create(HDC dc, int _width, int _height)
{
width = _width;
height = _height;
depth = 24;
bytes_per_line = ScanBytes(_width, 24);
bytes_per_pixel = PixelBytes(24);
InfoHeader.biSize = sizeof(BITMAPINFOHEADER);
InfoHeader.biWidth = _width;
InfoHeader.biHeight = _height;
InfoHeader.biBitCount = 24;
InfoHeader.biPlanes = 1;
InfoHeader.biXPelsPerMeter = 0;
InfoHeader.biYPelsPerMeter = 0;
InfoHeader.biClrUsed = 0;
InfoHeader.biClrImportant = 0;
InfoHeader.biCompression = BI_RGB;
InfoHeader.biSizeImage = bytes_per_line * _height;
Info =(BITMAPINFO*)&InfoHeader;
hBitmap = CreateDIBSection(dc, Info, DIB_RGB_COLORS, &Bits, NULL, 0);
return hBitmap != 0;
}
在调用Draw函数就可实现图像的显示
//绘制图形
void CPictureDraw::Draw(HDC dc, int x, int y, int w, int h, int ox, int oy)
{
HDC memdc = CreateCompatibleDC(dc);
HGDIOBJ oldbmp = SelectObject(memdc, hBitmap);
BitBlt(dc, x, y, w, h, memdc, ox, oy, SRCCOPY);
GdiFlush();
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
}
void CPictureDraw::Draw(HDC dc, RECT &rect, POINT &point)
{
Draw(dc, rect.left, rect.top, rect.right - rect.left,
rect.bottom - rect.top, point.x, point.y);
}