一个简单的windows位图文件类的实现(一)目的在图像处理的过程中,我们必然要对位图文件进行像素级的操作。一种办法是使用windows位图操作api或者是MFC中的CBitmap类;另外的办法是调用第三方的图像类,如CxImage类。作者在使用上述这些方案时,发现了如下的问题,首先,他们的功能要么过强(提供了许多一般很少用的功能,例如封装了很多我们不想要的图像格式处理函数),要么过弱(很多常用功能没有提供,比如对像素级的操作接口)。为此,作者根据自己的使用需要,建立了一个针对windows 24位真彩色位图的处理函数。功能目前还不是很完善,但是已经能够满足作者的使用需要。主要功能有:位图的读写、位图的建立和保存、两幅位图的比较、位图中像素颜色的获取和设置、位图中指定区域的获取和设置等等。如果有需要的话,可以进行进一步的的扩充。(二)位图处理概述1 位图在windows中以位图文件格式存放(bmp文件)。文件格式如下:[ 位图文件头 + 位图信息头 + 颜色表(24位真彩色没有这项) + 位图数据 ]2 位图文件头:位图文件头主要用于识别位图文件。以下是位图文件头结构的定义和含义:typedef struct tagBITMAPFILEHEADER{ WORD bfType; //必须是“BM”(0x4d42),标志该文件是位图文件 DWORD bfSize; //位图文件的总的大小 WORD bfReserved1; //必须为0 WORD bfReserved2; //必须为0 DWORD bfOffBits; //到位图数据的偏移量(字节数)} BITMAPFILEHEADER;3 位图信息头 + 颜色表以下结构体中定义了位图信息头和颜色表。typedef struct tagBITMAPINFO { BITMAPINFOHEADER bmiHeader;//信息头 RGBQUAD bmiColors[1];//颜色表} BITMAPINFO;本文中只讨论24位真彩色位图,因此上述结构体不予讨论,只关注信息头就可以了。信息头定义如下:typedef struct tagBITMAPINFOHEADER{ DWORD biSize; //等于sizeof(BITMAPINFOHEADER) LONG biWidth; //图像的宽度 LONG biHeight; //高度 WORD biPlanes; //必须为1 WORD biBitCount //每个像素的比特数,24位真彩色图应该设成24 DWORD biCompression; //压缩 DWORD biSizeImage; //图像数据的大小 LONG biXPelsPerMeter; //水平方向上的每米的像素个数 LONG biYPelsPerMeter; //垂直方向上的每米的像素个数 DWORD biClrUsed; //调色板中实际使用的颜色数 DWORD biClrImportant; //现实位图时必须的颜色数} BITMAPINFOHEADER; 4 位图数据根据不同的位图,位图数据所占据的字节数也是不同的,比如,对于8位位图,每个字节代表了一个像素,对于16位位图,每两个字节代表了一个像素,对于24位位图,每三个字节代表了一个像素,对于32位位图,每四个字节代表了一个像素。本文中的位图采用的是24位位图(3字节)。这里需要说明的是:位图数据中的数据排列不是按照我们习惯的从左到右,从上到下的顺序。而是采用了从下到上,从左到右的顺序排列每个像素,且,每个像素的RGB颜色值的排列顺序是B,G,R,恰好相反。因此,我们在处理的时候需要特别的注意。(三)MyColor类定义和实现MyColor类代表了一个像素点(的颜色),为了方便后续的处理,使用double类型保存RGB值。struct MyColor{ public: // default constructor MyColor() { } // double initialization constructor MyColor(double x, double y, double z) { X = x; Y = y; Z = z; } // int initialization constructor MyColor(int x, int y, int z) { X = x; Y = y; Z = z; } // Equal operator bool operator==(MyColor& c) { return ( (X == c.X) && (Y == c.Y) && (Z == c.Z)); } // Assignment operator MyColor& operator=(MyColor& c) { X = c.X; Y = c.Y; Z = c.Z; return (*this); } // operator / MyColor operator/(int i) { return MyColor(this->X / i, this->Y / i, this->Z / i); } MyColor operator/(double d) { return MyColor(this->X / d, this->Y / d, this->Z / d); } // operator * MyColor operator*(int i) { return MyColor(this->X * i, this->Y * i, this->Z * i); } MyColor operator*(double d) { return MyColor(this->X * d, this->Y * d, this->Z * d); } // operator + MyColor operator+(MyColor& c) { return MyColor(this->X + c.X, this->Y + c.Y, this->Z + c.Z); } // operator - double operator-(MyColor& c) { return _Euclidean3D(c); } // 3-D 欧式距离(squared) double _Euclidean3D(MyColor& c) { static double tmp; tmp = (this->X - c.X) * (this->X - c.X) + (this->Y - c.Y) * (this->Y - c.Y) + (this->Z - c.Z) * (this->Z - c.Z); return tmp; } public: // 3-dimension data, eg. R, G, B double X; double Y; double Z;};上面的“减号”操作符是求两个颜色值的距离,它直接调用 _Euclidean3D计算欧式距离;也可以替换成别的距离函数(如海明距离)。X,Y,Z中保存的是颜色值,double类型。还有一些别的操作,如*,/等等,主要是对颜色值进行线性运算。(四)MyBitmap类的定义MyBitmap类实现了基本的常用的位图操作。一些成员变量:LPBITMAPFILEHEADER m_lpFileHeader;//文件头的指针LPBITMAPINFOHEADER m_lpInfoHead;//信息头指针MyColor * m_Image;//位图数据指针一些成员函数:void Open(char* Filename);//打开文件(24位真彩色)void Close();//关闭文件,释放所有资源void Write2File(char* Filename);//写回bmp文件void Create(int width, int height, MyColor c = MyColor(0,0,0));//建立指定大小和颜色的位图对象MyBitmap& operator=(MyBitmap& bitmap);//复制本对象bool operator==(MyBitmap& bitmap);//比较两个位图是否相同void GetAt(int x, int y, MyColor& c);//获取坐标x,y的颜色值(左上角为0,0)void SetAt(int x, int y, MyColor& c);//设置颜色MyColor& GetRef(int x, int y);//获取某个像素对象的引用void GetImageSection(int x, int y, int width, int height, MyBitmap& bitmap);//获取指定区域void SetImageSection(int x, int y, int width, int height, MyColor& c);//设置指定区域void SetImageSection(int x, int y, int width, int height, MyBitmap& bitmap);//设置指定区域long GetWidth();//获取宽度long GetHeight();//获取高度bool IsBitmapValid();//测试对象是否有效 // 24-bit bitmap class class MyBitmap { public: // default constructor MyBitmap(); // initialization constructor MyBitmap(int width, int height, MyColor c = MyColor(0,0,0)); // default deconstructor ~MyBitmap(); public: // open a 24-bit bitmap file void Open(char* Filename); // close a bitmap object and release all resource it used void Close(); // write this object to a bitmap file void Write2File(char* Filename); // create a new bitmap object with prefixed width and height and color void Create(int width, int height, MyColor c = MyColor(0,0,0)); // assignment operator MyBitmap& operator=(MyBitmap& bitmap); // equal operator bool operator==(MyBitmap& bitmap); // get color element at pos(x,y) x: width y: height (0,0) at left-top void GetAt(int x, int y, MyColor& c); // set color element at pos(x,y) void SetAt(int x, int y, MyColor& c); // get reference of the m_Image MyColor& GetRef(int x, int y); // get section of an image void GetImageSection(int x, int y, int width, int height, MyBitmap& bitmap); // set section of an image to color void SetImageSection(int x, int y, int width, int height, MyColor& c); // set section of an image to another image void SetImageSection(int x, int y, int width, int height, MyBitmap& bitmap); // get bitmap width long GetWidth(); // get bitmap height long GetHeight(); // test if a valid bitmap is read in memory bool IsBitmapValid(); private: // get bytes used per line in the bitmap int GetBytesPerLine(); private: // true if an object is valid bool m_bValid; // store the image file head LPBITMAPFILEHEADER m_lpFileHeader; // store the image info head LPBITMAPINFOHEADER m_lpInfoHead; // store the image data MyColor * m_Image; };(五)MyBitmap类的定义以下是实现代码: MyBitmap::MyBitmap() { m_bValid = false; m_lpFileHeader = NULL; m_lpInfoHead = NULL; m_Image = NULL; } MyBitmap::MyBitmap(int width, int height, MyColor c /* = MyColor */) { if(!(width > 0 && height > 0)) throw "MyBitmap::MyBitmap - width / height non-positive"; m_bValid = false; m_lpFileHeader = NULL; m_lpInfoHead = NULL; m_Image = NULL; Create(width, height, c); } MyBitmap::~MyBitmap() { delete m_lpFileHeader; delete m_lpInfoHead; delete []m_Image; m_bValid = false; m_lpFileHeader = NULL; m_lpInfoHead = NULL; m_Image = NULL; } bool MyBitmap::IsBitmapValid() { return m_bValid; } void MyBitmap::Open(char* Filename) { if(IsBitmapValid()) Close(); FILE *fp = fopen(Filename, "rb"); if(!fp) throw "MyBitmap::Open - file open error"; m_lpFileHeader = new BITMAPFILEHEADER; if(m_lpFileHeader == NULL) throw "MyBitmap::Open - out of memory"; m_lpInfoHead = new BITMAPINFOHEADER; if(m_lpInfoHead == NULL) throw "MyBitmap::Open - out of memory"; fread(m_lpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp); fread(m_lpInfoHead, sizeof(BITMAPINFOHEADER), 1, fp); byte *image = new byte[m_lpInfoHead->biSizeImage]; if(image == NULL) throw "MyBitmap::Open - out of memory"; fread(image, 1, m_lpInfoHead->biSizeImage, fp); m_Image = new MyColor[m_lpInfoHead->biWidth * m_lpInfoHead->biHeight]; if(m_Image == NULL) throw "MyBitmap::Open - out of memory"; double thex, they, thez; long addr; long bytePerline = m_lpInfoHead->biSizeImage / m_lpInfoHead->biHeight; for(int x = 0 ; x < m_lpInfoHead->biWidth ; x++) { for(int y = 0 ; y < m_lpInfoHead->biHeight ; y++) { addr = x * 3 + (m_lpInfoHead->biHeight - y - 1) * bytePerline; thez = image[addr]; they = image[addr+1]; thex = image[addr+2]; m_Image[y * m_lpInfoHead->biWidth + x].X = thex; m_Image[y * m_lpInfoHead->biWidth + x].Y = they; m_Image[y * m_lpInfoHead->biWidth + x].Z = thez; } } delete []image; m_bValid = true; } void MyBitmap::Close() { if(m_bValid) { delete m_lpFileHeader; m_lpFileHeader = NULL; delete m_lpInfoHead; m_lpInfoHead = NULL; delete []m_Image; m_Image = NULL; m_bValid = false; } } int MyBitmap::GetBytesPerLine() { if(!IsBitmapValid()) throw "MyBitmap::GetBytesPerLine - open first"; int bytePerline = m_lpInfoHead->biSizeImage / m_lpInfoHead->biHeight; return bytePerline; } long MyBitmap::GetWidth() { if(!IsBitmapValid()) throw "MyBitmap::GetWidth - open first"; return m_lpInfoHead->biWidth; } long MyBitmap::GetHeight() { if(!IsBitmapValid()) throw "MyBitmap::GetHeight - open first"; return m_lpInfoHead->biHeight; } void MyBitmap::Write2File(char* Filename) { if(!IsBitmapValid()) throw "MyBitmap::Write2File - no image exists"; FILE *fp = fopen(Filename, "w+b"); if(!fp) throw "MyBitmap::Write2File - file open error"; fwrite(m_lpFileHeader, sizeof(BITMAPFILEHEADER), 1, fp); fwrite(m_lpInfoHead, sizeof(BITMAPINFOHEADER), 1, fp); byte *image = new byte[m_lpInfoHead->biSizeImage]; if(image == NULL) throw "MyBitmap::Write2File - out of memory"; memset(image, 0, m_lpInfoHead->biSizeImage); double thex, they, thez; long addr; long bytePerline = this->GetBytesPerLine(); for(int x = 0 ; x < m_lpInfoHead->biWidth ; x++) { for(int y = 0 ; y < m_lpInfoHead->biHeight ; y++) { addr = x * 3 + (m_lpInfoHead->biHeight - y - 1) * bytePerline; thex = m_Image[y * m_lpInfoHead->biWidth + x].X; they = m_Image[y * m_lpInfoHead->biWidth + x].Y; thez = m_Image[y * m_lpInfoHead->biWidth + x].Z; image[addr] = thez; image[addr+1] = they; image[addr+2] = thex; } } fwrite(image, 1, m_lpInfoHead->biSizeImage, fp); delete []image; fclose(fp); } void MyBitmap::Create(int width, int height, MyColor c) { if(!(width > 0 && height > 0)) throw "MyBitmap::Create - width / height non-positive"; if(this->IsBitmapValid()) Close(); m_lpFileHeader = new BITMAPFILEHEADER; if(m_lpFileHeader == NULL) throw "MyBitmap::Create - out of memory"; memset(m_lpFileHeader, 0, sizeof(BITMAPFILEHEADER)); m_lpInfoHead = new BITMAPINFOHEADER; if(m_lpInfoHead == NULL) throw "MyBitmap::Create - out of memory"; memset(m_lpInfoHead, 0, sizeof(BITMAPINFOHEADER)); m_Image = new MyColor[width * height]; if(m_Image == NULL) throw "MyBitmap::Create - out of memory"; int linebytes = width * 3; while(linebytes % 4) linebytes ++; //位图文件大小 int ImageSize = linebytes * height + sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); m_lpFileHeader->bfType = (WORD)0x4d42; m_lpFileHeader->bfSize = ImageSize; m_lpFileHeader->bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); m_lpInfoHead->biBitCount = 24; m_lpInfoHead->biWidth = width; m_lpInfoHead->biHeight = height; m_lpInfoHead->biPlanes = 1; m_lpInfoHead->biCompression = BI_RGB; m_lpInfoHead->biSize = sizeof(BITMAPINFOHEADER); m_lpInfoHead->biSizeImage = linebytes * height; for(int x = 0 ; x < width ; x ++) { for(int y = 0 ; y < height ; y ++) { m_Image[width * y + x] = c; } } m_bValid = true; } MyBitmap& MyBitmap::operator=(MyBitmap& bitmap) { if(bitmap.IsBitmapValid()) //valid { this->Close(); m_lpFileHeader = new BITMAPFILEHEADER; if(m_lpFileHeader == NULL) throw "MyBitmap::operator= - out of memory"; memcpy(m_lpFileHeader, bitmap.m_lpFileHeader, sizeof(BITMAPFILEHEADER)); m_lpInfoHead = new BITMAPINFOHEADER; if(m_lpInfoHead == NULL) throw "MyBitmap::operator= - out of memory"; memcpy(m_lpInfoHead, bitmap.m_lpInfoHead, sizeof(BITMAPINFOHEADER)); m_Image = new MyColor[bitmap.GetWidth() * bitmap.GetHeight()]; if(m_Image == NULL) throw "MyBitmap::operator= - out of memory"; memcpy(m_Image, bitmap.m_Image, bitmap.GetWidth() * bitmap.GetHeight() * sizeof(MyColor)); m_bValid = true; } else //not valid { this->Close(); } return (*this); } bool MyBitmap::operator==(MyBitmap& bitmap) { if(this->m_bValid == false && bitmap.m_bValid == false) return true; if( (this->m_bValid == true && bitmap.m_bValid == false) || (this->m_bValid == false && bitmap.m_bValid == true) ) return false; //all valid int ret; ret = memcmp(this->m_lpFileHeader, bitmap.m_lpFileHeader, sizeof(BITMAPFILEHEADER)); if(ret != 0) return false; ret = memcmp(this->m_lpInfoHead, bitmap.m_lpInfoHead, sizeof(BITMAPINFOHEADER)); if(ret != 0) return false; ret = memcmp(this->m_Image, bitmap.m_Image, sizeof(MyColor) * bitmap.GetWidth() * bitmap.GetHeight()); if(ret != 0) return false; return true; } void MyBitmap::GetAt(int x, int y, MyColor& c) { if( ! (x >= 0 && y >= 0 && x < this->GetWidth() && y < this->GetHeight()) ) throw "MyBitmap::GetAt - x / y beyond the bound"; if(!IsBitmapValid()) throw "MyBitmap::GetAt - no image exists"; c = m_Image[this->GetWidth() * y + x]; } void MyBitmap::SetAt(int x, int y, MyColor& c) { if( ! (x >= 0 && y >= 0 && x < this->GetWidth() && y < this->GetHeight()) ) throw "MyBitmap::SetAt - x / y beyond the bound"; if(!IsBitmapValid()) throw "MyBitmap::SetAt - no image exists"; m_Image[this->GetWidth() * y + x] = c; } MyColor& MyBitmap::GetRef(int x, int y) { if( ! (x >= 0 && y >= 0 && x < this->GetWidth() && y < this->GetHeight()) ) throw "MyBitmap::GetRef - x / y beyond the bound"; if(!IsBitmapValid()) throw "MyBitmap::GetRef - no image exists"; return m_Image[this->GetWidth() * y + x]; } void MyBitmap::GetImageSection(int x, int y, int width, int height, MyBitmap& bitmap) { if( ! (x >= 0 && y >= 0 && x+width-1 < this->GetWidth() && y+height-1 < this->GetHeight() && width > 0 && height > 0) ) throw "MyBitmap::GetImageSection - x / y / widht / height beyond the bound"; bitmap.Create(width, height); MyColor c; for(int i = 0 ; i < width ; i ++) { for(int j = 0 ; j < height ; j ++) { this->GetAt(i + x, j + y, c); bitmap.SetAt(i, j, c); } } } void MyBitmap::SetImageSection(int x, int y, int width, int height, MyColor& c) { if( ! (x >= 0 && y >= 0 && x+width-1 < this->GetWidth() && y+height-1 < this->GetHeight() && width > 0 && height > 0) ) throw "MyBitmap::SetImageSection - x / y / widht / height beyond the bound"; for(int i = 0 ; i < width ; i ++) { for(int j = 0 ; j < height ; j ++) { this->GetRef(i + x, j + y) = c; } } } void MyBitmap::SetImageSection(int x, int y, int width, int height, MyBitmap& bitmap) { if( ! (x >= 0 && y >= 0 && x+width-1 < this->GetWidth() && y+height-1 < this->GetHeight() && width > 0 && height > 0 && width <= bitmap.GetWidth() && height <= bitmap.GetHeight()) ) throw "MyBitmap::SetImageSection - x / y / widht / height beyond the bound"; MyColor c; for(int j = 0 ; j < height ; j ++) { for(int i = 0 ; i < width ; i ++) { bitmap.GetAt(i, j, c); this->GetRef(i + x, j + y) = c; } } } (六)测试 主函数: void main(void) { MyBitmap b; b.Open("a.bmp"); b.Write2File("b.bmp"); b.Close(); } (七)说明 本文实现了一个简单易用的24位真彩色位图类,避免了过多的接口和不必要的功能。通过对这个类进行扩充,还可以满足未来更多的需要。本文引用通告地址: http://blog.csdn.net/hitjinming/services/trackbacks/425536.aspx
|