分享
 
 
 

[原创] 一个简单的windows位图文件类的实现

王朝system·作者佚名  2006-01-31
窄屏简体版  字體: |||超大  

[原创] 一个简单的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位真彩色位图类,避免了过多的接口和不必要的功能。通过对这个类进行扩充,还可以满足未来更多的需要。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有