目录
一、图像的概念:
图像:
图像的矩阵表示:
颜色表:
彩色空间有三种:
二、BMP文件结构及存取
三、GDI对象及GDI位图
1、从Resource View资源中装入GDI位图
对位图拉伸:使用CBitmap中的StretchBlt()
四、设备无关位图(DIB)
调色板:
一个窗口中显示一个DIB过程??:
DIB访问函数:
五、面向过程的DIB的读写及访问:
面向对象的DIB读写及访问-构建ImgCenterDib类:
使用ImgCenterDib类实现多文档应用程序图像的可视化编程
六、面向对象的图像处理算法实现
代码github地址:
https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git.
图像是像素点的集合
灰度图像用二维矩阵表示
图像的颜色记录靠颜色表来完成,颜色表是一个存储颜色信息的数据库,依靠索引获取颜色;
特别的,对于真彩色图像,每个像素占3个存储空间(24位)分别对于RGB,图像本身已包含颜色信息,无需颜色表
1、RGB:rgb以不同比例的线性运算构成彩色信息,有源物体一般采用RGB空间,rgb三基色相加模型
2、CMY:无源物体采用CMY(Cyan/Magenta/Yellow,青/洋红/黄)三减色模型表示,如油墨、颜料、彩色打印机
3、HSI:(Hue/Saturation/intensity,色调/饱和度/强度)模型
色调Hue:表示颜色信息,将红(0)、黄(60)、绿(120)、青(180)、蓝(240)、洋红(300)用角度表示;
饱和度Saturation:表示色的纯度,0%(灰色光或白光)-100%(纯色光);
强度Intensity:表示彩色光颜色的强弱程度,用亮度表示。
色调与饱和度称为色度,表示颜色的类别与深浅程度。
具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git
bmp图像结构的构成:
位图文件头、位图信息头、颜色表、位图数据四部分构成。
在c++中,四部分分别由结构体进行实现。
注意:
1、bmp的扫描每行所占的字节数必须是4的倍数,不足4的倍数要进行扩充
2、bmp图像的坐标原点在左下角,读取方式是从下到上、从左到右
bmp文件的读取:
读入:readBmp()
存盘:saveBmp()
bmp位图数据访问:
bmp利用readBmp读入图像,又用saveBmp写入磁盘,在读入图像之后,写入磁盘之前这段时间,数据是存在在内存中的,我们可以利用这个时间来对图像中的数据进行修改(访问)
灰度图像是需要颜色表来获取颜色信息的,但是由于灰度图像的颜色表中RGB分量相等都是0-255,因此只有亮度信息,没有颜色信息;如果对灰度图像的颜色表进行改变,则灰度图像就有了颜色信息,但是这个颜色信息并非真实世界中的颜色。
win sdk BMP文件读写:
#include "stdio.h"
#include "Windows.h"
//几个全局变量,存放读入图像的位图数据、宽、高、颜色表及每像素所占位数(比特)
//此处定义全局变量主要为了后面的图像数据访问及图像存储作准备
unsigned char *pBmpBuf;//读入图像数据的指针
int bmpWidth;//图像的宽
int bmpHeight;//图像的高
RGBQUAD *pColorTable;//颜色表指针
int biBitCount;//图像类型
/***********************************************************************
* 函数名称:
* readBmp()
*
*函数参数:
* char *bmpName -文件名字及路径
*
*返回值:
* 0为失败,1为成功
*
*说明:给定一个图像文件名及其路径,读图像的位图数据、宽、高、颜色表及每像素
* 位数等数据进内存,存放在相应的全局变量中
***********************************************************************/
bool readBmp(char *bmpName)
{
//二进制读方式打开指定的图像文件
FILE *fp=fopen(bmpName,"rb");
if(fp==0) return 0;
//跳过位图文件头结构BITMAPFILEHEADER
fseek(fp, sizeof(BITMAPFILEHEADER),0);
//定义位图信息头结构变量,读取位图信息头进内存,存放在变量head中
BITMAPINFOHEADER head;
fread(&head, sizeof(BITMAPINFOHEADER), 1,fp);
//获取图像宽、高、每像素所占位数等信息
bmpWidth = head.biWidth;
bmpHeight = head.biHeight;
biBitCount = head.biBitCount;
//定义变量,计算图像每行像素所占的字节数(必须是4的倍数)
int lineByte=(bmpWidth * biBitCount/8+3)/4*4;
//灰度图像有颜色表,且颜色表表项为256
if(biBitCount==8){
//申请颜色表所需要的空间,读颜色表进内存
pColorTable=new RGBQUAD[256];
fread(pColorTable,sizeof(RGBQUAD),256,fp);
}
//申请位图数据所需要的空间,读位图数据进内存
pBmpBuf=new unsigned char[lineByte * bmpHeight];
fread(pBmpBuf,1,lineByte * bmpHeight,fp);
//关闭文件
fclose(fp);
return 1;
}
/***********************************************************************
* 函数名称:
* saveBmp()
*
*函数参数:
* char *bmpName -文件名字及路径
* unsigned char *imgBuf -待存盘的位图数据
* int width -像素为单位待存盘位图的宽
* int height -像素为单位待存盘位图高
* int biBitCount -每像素所占位数
* RGBQUAD *pColorTable -颜色表指针
*返回值:
* 0为失败,1为成功
*
*说明:给定一个图像位图数据、宽、高、颜色表指针及每像素所占的位数等信息,
* 将其写到指定文件中
***********************************************************************/
bool saveBmp(char *bmpName, unsigned char *imgBuf, int width, int height,
int biBitCount, RGBQUAD *pColorTable)
{
//如果位图数据指针为0,则没有数据传入,函数返回
if(!imgBuf)
return 0;
//颜色表大小,以字节为单位,灰度图像颜色表为1024字节,彩色图像颜色表大小为0
int colorTablesize=0;
if(biBitCount==8)
colorTablesize=1024;
//待存储图像数据每行字节数为4的倍数
int lineByte=(width * biBitCount/8+3)/4*4;
//以二进制写的方式打开文件
FILE *fp=fopen(bmpName,"wb");
if(fp==0) return 0;
//申请位图文件头结构变量,填写文件头信息
BITMAPFILEHEADER fileHead;
fileHead.bfType = 0x4D42;//bmp类型
//bfSize是图像文件4个组成部分之和
fileHead.bfSize= sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER)
+ colorTablesize + lineByte*height;
fileHead.bfReserved1 = 0;
fileHead.bfReserved2 = 0;
//bfOffBits是图像文件前三个部分所需空间之和
fileHead.bfOffBits=54+colorTablesize;
//写文件头进文件
fwrite(&fileHead, sizeof(BITMAPFILEHEADER),1, fp);
//申请位图信息头结构变量,填写信息头信息
BITMAPINFOHEADER head;
head.biBitCount=biBitCount;
head.biClrImportant=0;
head.biClrUsed=0;
head.biCompression=0;
head.biHeight=height;
head.biPlanes=1;
head.biSize=40;
head.biSizeImage=lineByte*height;
head.biWidth=width;
head.biXPelsPerMeter=0;
head.biYPelsPerMeter=0;
//写位图信息头进内存
fwrite(&head, sizeof(BITMAPINFOHEADER),1, fp);
//如果灰度图像,有颜色表,写入文件
if(biBitCount==8)
fwrite(pColorTable, sizeof(RGBQUAD),256, fp);
//写位图数据进文件
fwrite(imgBuf, height*lineByte, 1, fp);
//关闭文件
fclose(fp);
return 1;
}
/*
//文件存取函数调用的例子
void main()
{
//读入指定BMP文件进内存
char readPath[]="dog.BMP";
readBmp(readPath);
//输出图像的信息
printf("width=%d,height=%d,biBitCount=%d\n",bmpWidth,bmpHeight,biBitCount);
//将图像数据存盘
char writePath[]="dogcpy.BMP";
saveBmp(writePath, pBmpBuf, bmpWidth, bmpHeight, biBitCount, pColorTable);
//清除缓冲区,pBmpBuf和pColorTable是全局变量,在文件读入时申请的空间
delete []pBmpBuf;
if(biBitCount==8)
delete []pColorTable;
}
*/
/*
//文件数据进行修改的例子
void main()
{
//读入指定BMP文件进内存
char readPath[]="dog.BMP";
readBmp(readPath);
//输出图像的信息
printf("width=%d,height=%d,biBitCount=%d\n",bmpWidth,bmpHeight,biBitCount);
//循环变量,图像的坐标
int i,j;
//每行字节数
int lineByte=(bmpWidth*biBitCount/8+3)/4*4;
//循环变量,针对彩色图像,遍历每像素的三个分量
int k;
//将图像左下角1/4部分置成黑色
if(biBitCount==8){//对于灰度图像
for(i=0;i
源码下载:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git
GDI(graphics device interface)图像设备接口的缩写,
GDI对象类型在windows中是通过MFC(Microsoft基础类库)中的类表示的
CGdiobject是所有GDI对象类的抽象基类,
即GDI对象类型在windows中是通过CGdiobject派生类的c++对象实现的。
CGdiobject派生类的c++对象有:
使用CBitmap对象步骤:
从资源中直接获取位图是使用位图最简单的方法。
从Workspace窗口里的Resource Viewz中,打开工程位图资源列表
其中,IDB_building是位图资源ID,打开IDB_building位图资源管理器对话框,
看到对应的位图文件位于工程文件res目录下的building.bmp,
由于资源位图不支持24位真彩色,所以此图是256色的索引位图。
将资源的DIB转换为GFI位图:使用CBitmap中的LoadBitmap()函数
将内存中图像数据复制到显示器设备环境中:使用CBitmap中的BitBlt()函数
/
// CChap1_2View drawing
void CChap1_2View::OnDraw(CDC* pDC)
{
//CBitmap对象
CBitmap bitmap;
//设备环境类对象
CDC dcMemory;
//加载资源位图
bitmap.LoadBitmap(IDB_building);
//创建内存设备环境
dcMemory.CreateCompatibleDC(pDC);
//把位图选进内存设备环境,并保存旧的GDI位图对象,
CBitmap *oldbitmap=dcMemory.SelectObject(&bitmap);
//显示
pDC->BitBlt(0,0,400,300, &dcMemory,0,0,SRCCOPY);
//释放bitmap,恢复原GDI位图
dcMemory.SelectObject(oldbitmap);
}
结果显示:
// CChap1_3View drawing
void CChap1_3View::OnDraw(CDC* pDC)
{
//CBitmap对象
CBitmap bitmap;
//设备环境类对象
CDC dcMemory;
//加载资源位图
bitmap.LoadBitmap(IDB_building);
//创建内存设备环境
dcMemory.CreateCompatibleDC(pDC);
//把位图选进内存设备环境,并保存旧的GDI位图对象,
CBitmap *oldbitmap=dcMemory.SelectObject(&bitmap);
//将128*128的图像伸缩显示在以(0,0)点开始的450*250的一个区域内
pDC->StretchBlt(0,0,450,250, &dcMemory,0,0,400,300,SRCCOPY);
//释放bitmap,恢复原GDI位图
dcMemory.SelectObject(oldbitmap);
}
拉伸结果
具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git
BMP文件包括DIB格式和DDB格式。
DDB(device-dependent bitmap):设备相关位图,存储与某个硬件设备(显示设备或打印设备)内存兼容的不压缩图像,依赖于硬件,不常用。
DIB(device-independent bitmap):设备无关位图,与设备无关,自带颜色信息、颜色表管理容易,具有很强通用性。
如果调用内存中的DIB位图?:
DIB位图有位图信息头、颜色表、位图数据三部分构成,没有位图文件头,是脱离文件存在的。
一种GDI对象,是颜色映射接口;
将颜色映射到显示器或打印机;
可以使应用程序在不干扰其他应用程序前提下,充分利用输出设备的颜色描述能力
当我们需要显示DIB位图时,需要根据DIB位图的颜色表生产调色板。
window支持一些重要的DIB访问函数,但是没有被封装到MFC中,可以利用一下函数实现:
利用上面的windows自带的DIB访问函数,可以实现位图的可视化编程。
现在我们编写一个基于单文档、面向过程的位图显示与存储程序。
在基于MFC的文档-视图结果应用程序中,需要遵守两个原则:
原始图显示:
void CChap1_4View::OnDraw(CDC* pDC)
{
//获取文档类句柄
CChap1_4Doc* pDoc = GetDocument();
ASSERT_VALID(pDoc);
if(pDoc->m_pDib==NULL)
return;
//定义infoHead变量指向DIB中的BITMAPINFOHEADER结构
BITMAPINFOHEADER *infoHead=(BITMAPINFOHEADER*)pDoc->m_pDib;
//获取DIB的宽、高、所占位数
int width=infoHead->biWidth;
int height=infoHead->biHeight;
int biBitCount=infoHead->biBitCount;
//求颜色表的长度,彩色图像颜色表长度为0,非彩色图像(灰度图像)
// 颜色表长度为pow(2,biBitCount)
int colorTableLng;
if(biBitCount!=24)
colorTableLng=pow(2,biBitCount);
else
colorTableLng=0;
//如果有颜色表,则创建调色板,hPalette为新创建的调色板句柄,
//hOldPal旧的调色板句柄
HPALETTE hPalette=0, hOldPal;
if(colorTableLng!=0){
//定义颜色表指针pColorTable,指向DIB的颜色表
RGBQUAD *pColorTable=(RGBQUAD *)(pDoc->m_pDib+
sizeof(BITMAPINFOHEADER));
//申请空间,生成LOGPALETTE结构
LPLOGPALETTE pLogPal = (LPLOGPALETTE)new char[2*sizeof(WORD)
+colorTableLng * sizeof(PALETTEENTRY)];
pLogPal->palVersion = 0x300;
pLogPal->palNumEntries =colorTableLng;
for(int i = 0; i < colorTableLng; i++) {
pLogPal->palPalEntry[i].peRed = pColorTable[i].rgbRed;
pLogPal->palPalEntry[i].peGreen =pColorTable[i].rgbGreen;
pLogPal->palPalEntry[i].peBlue = pColorTable[i].rgbBlue;
pLogPal->palPalEntry[i].peFlags = 0;
}
//创建逻辑调色板
hPalette = ::CreatePalette(pLogPal);
//将调色板选入系统
hOldPal=::SelectPalette(pDC->GetSafeHdc(), hPalette, TRUE);
//实现调色板
pDC->RealizePalette();
//清理空间
delete []pLogPal;
}
//DIB显示所需要的模式
pDC->SetStretchBltMode(COLORONCOLOR);
//pImgData指向DIB的位图数据
unsigned char* pImgData=(unsigned char*)(pDoc->m_pDib+
sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD) * colorTableLng);
//显示DIB到显示器
::StretchDIBits(pDC->GetSafeHdc(), 0, 0, width, height,
0, 0, width, height, pImgData,
(LPBITMAPINFO)(pDoc->m_pDib), DIB_RGB_COLORS, SRCCOPY);
//恢复原调色板
if(hOldPal!=NULL)
::SelectPalette(pDC->GetSafeHdc(), hOldPal, TRUE);
}
位图访问数据后修改显示:
void CChap1_4View::OnDataAccess()
{
//获取文档类句柄
CChap1_4Doc* pDoc = GetDocument();
//如果DIB为空,则返回
if(pDoc->m_pDib==NULL)
return;
//定义infoHead变量指向DIB中的BITMAPINFOHEADER结构
BITMAPINFOHEADER *infoHead=(BITMAPINFOHEADER*)pDoc->m_pDib;
//获取DIB的宽、高、所占位数
int width=infoHead->biWidth;
int height=infoHead->biHeight;
int biBitCount=infoHead->biBitCount;
//每行像素所占字节数,必须是4的倍数
int lineByte=(width*biBitCount/8+3)/4*4;
//求颜色表的长度,彩色图像颜色表长度为0,非彩色图像(灰度图像)
// 颜色表长度为pow(2,biBitCount)
int colorTableLng;
if(biBitCount!=24)
colorTableLng=pow(2,biBitCount);
else
colorTableLng=0;
//pImgData指向DIB的位图数据
unsigned char* pImgData=(unsigned char*)(pDoc->m_pDib+
sizeof(BITMAPINFOHEADER)+sizeof(RGBQUAD) * colorTableLng);
//以下将图像数据左下角1/4置成黑色
//循环变量,图像的坐标
int i, j;
if(biBitCount==8){//灰度图像
for(i=0;i
源码:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git
上面利用面向过程的方式实现了图像的可视化编程;
由于MFC类中没有封装DIB类,我们可以自己封装类来实现面向对象的DIB读写及访问。
我们声明的类是ImgCenterDib(Image Center Dib,图像处理中心编写的DIB);
ImgCenterDib(里面封装了DIB位图处理所需要的基本的成员变量和成员函数);
以后的算法实现都依该类为基类派生(充分利用了面向对象的封装、继承特点,有使代码易于维护和移植)
原始图像进行数据访问:
void CChap1_5View::OnDataAccess1()
{
//获取文档类指针
CChap1_5Doc *pDoc=GetDocument();
//获取ImgCenterDib类对象的指针,访问打开文件的数据
ImgCenterDib *pDib=pDoc->GetPDib();
//位图数据的指针
unsigned char *imgData=pDib->m_pImgData;
//位图阵列的大小
CSize imgSize=pDib->GetDimensions();
//每像素的位数
int nBitCount=pDib->m_nBitCount;
//循环变量,图像坐标
int i,j;
//每像素占字节数
int bytePerPixel=nBitCount/8;
//每行像素所占字节数,必须是4的倍数
int lineByte=(imgSize.cx*nBitCount/8+3)/4*4;
//循环变量,每像素各字节访问的循环变量
int k;
//将图像左下角1/4部分置黑色
for(i=0;i
新建视图进行数据访问:
void CChap1_5View::OnDataAccess2()
{
//获取文档类指针
CChap1_5Doc *pDoc=GetDocument();
//获取ImgCenterDib类对象的指针,访问打开文件的数据
ImgCenterDib *pDib=pDoc->GetPDib();
//位图数据的指针
unsigned char *imgData=pDib->m_pImgData;
//位图阵列的大小
CSize imgSize=pDib->GetDimensions();
//每像素的位数
int nBitCount=pDib->m_nBitCount;
//每像素占字节数
int bytePerPixel=nBitCount/8;
//每行像素所占字节数,必须是4的倍数
int lineByte=(imgSize.cx*nBitCount/8+3)/4*4;
//申请缓冲区pBuf
unsigned char* pBuf=new unsigned char[lineByte*imgSize.cy];
//将原DIB位图数据拷贝至pBuf
memcpy(pBuf, imgData, lineByte*imgSize.cy);
//循环变量,图像坐标
int i,j;
//循环变量,每像素各字节访问的循环变量
int k;
//将pBuf左下角1/4部分置黑色
for(i=0;im_pMainWnd);
//发送一个新建文件的消息,创建一个新的文档-视图
pFrame->SendMessage(WM_COMMAND, ID_FILE_NEW);
//获取新建视图指针
CChap1_5View* pView=(CChap1_5View*)pFrame->MDIGetActive()->GetActiveView();
//获取相关联的新的文档类指针
CChap1_5Doc* pDocNew=pView->GetDocument();
//获取新文档中的ImgCenterDib类对象指针
ImgCenterDib *dibNew=pDocNew->GetPDib();
//调用ReplaceDib,用改变以后的位图数据替换原位图,
dibNew->ReplaceDib(imgSize,nBitCount,pDib->m_lpColorTable, pBuf);
//文档数据置脏,提示存盘信息
pDocNew->SetModifiedFlag(TRUE);
//各视图刷新显示
pDocNew->UpdateAllViews(pView);
}
结果:
以ImgCenterDib类为基类派生一个具有特效显示功能的特效显示类,并实现四种图像特效显示效果。
void SpecialEffectShow::Scan(CDC *pDC) //扫描显示一幅图象
{
int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位
CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
CBrush brush(RGB(255,255,255)); //设置画刷为白色
pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果
LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型
for(int j=0;jGetSafeHdc(),
0, j, bitmapWidth, 1,
0, bitmapHeight-j, bitmapWidth, 1,
m_pImgData, pBitmapInfo,
DIB_RGB_COLORS, SRCCOPY);
Sleep(3);//设置延时时间
}
}
void SpecialEffectShow::Slide(CDC *pDC)
{
int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位
CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
CBrush brush(RGB(255,255,255)); //设置画刷为白色
pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果
LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型
for(int i=0;i<=bitmapWidth;i++)//滑动特效显示的具体算法
{
for(int j=0;j<=bitmapHeight;j=j+8)
{
::StretchDIBits(pDC->GetSafeHdc(),
0, j-8 , i+1, 8,
bitmapWidth-i, bitmapHeight-j, i+1, 8,
m_pImgData, pBitmapInfo,
DIB_RGB_COLORS, SRCCOPY);
}
Sleep(3);//设置延时时间
}
}
void SpecialEffectShow::FadeIn(CDC *pDC)
{
int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位
CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
CBrush brush(RGB(0,0,0)); //设置画刷为黑色
pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成黑色,达到刷新屏幕的效果
LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型
int lineByte=(m_imgWidth*m_nBitCount/8+3)/4*4;//每行像素占字节数,必须为4的倍数
LPBYTE temp =new BYTE[bitmapHeight*lineByte];//在堆上分配内存存储临时图象数据
memset (temp,0,bitmapHeight*lineByte);//初始置零
for(int m=0;m<256;m++)
{
for(int j = 0; j < bitmapHeight; j++)
{
for(int i = 0; i < lineByte; i ++)
temp[j*lineByte+i]=m_pImgData[j*lineByte+i]*m/256;
}
::StretchDIBits(pDC->GetSafeHdc(),
0, 0, bitmapWidth, bitmapHeight,
0, 0, bitmapWidth, bitmapHeight,
temp, pBitmapInfo,DIB_RGB_COLORS, SRCCOPY);
Sleep(3);//设置延时时间
}
delete [] temp;//释放堆上分配的内存
}
void SpecialEffectShow::Mosaik(CDC *pDC)
{
int bitmapWidth=GetDimensions().cx;//获得源图象的宽度,以象素为单位
int bitmapHeight=GetDimensions().cy;//获得源图象的高度,以象素为单位
CRect rect(0,0,bitmapWidth,bitmapHeight);//以源图象的尺寸创建一个矩形
CBrush brush(RGB(255,255,255)); //设置画刷为白色
pDC->FillRect(&rect,&brush);//将已经显示出来的原图象重新设置成白色,达到刷新屏幕的效果
LPBITMAPINFO pBitmapInfo=(BITMAPINFO*)m_lpBmpInfoHead;
//为适应StretchDIBits函数的需要,将图像信息头指针强制转换为LPBITMAPINFO类型
//马赛克的大小设置为宽高都是12个像素
int WithFalg=0; //图象宽是12的整数倍的标志
int HeightFlag=0;//图象高是12的整数倍的标志
if((bitmapWidth%12)!=0)WithFalg=1;
if((bitmapHeight%12)!=0)HeightFlag=1;
long ArrayLength=(bitmapWidth/12+WithFalg)*(bitmapHeight/12+HeightFlag);
//将图象宽高都延拓至12的整数倍,然后将图象分成12X12的小块,按行顺序排列成一个数组
struct Square
{
CPoint pt;//记录每个12X12的小块的左上角坐标
bool HitFlag;//当随机扫描所有小块的时候,记录是否曾经被扫描过
};//自己定义一个结构,由一个Cpoint类型和一个击中标志构成
Square* SquareArray=new Square[ArrayLength];
int x=0;
int y=0;
for(int i=0; ibitmapWidth)
{x=0;y=y+12;}
//初始所有小块都未被击中过
SquareArray[i].HitFlag=FALSE;
}
long RandNum;//随即变量
srand( (unsigned)time( NULL ) );//生成随机种子
int i;
for(i=0;i<=ArrayLength;i++)
{
do
{
RandNum=(long)( ( (double)ArrayLength )*rand()/RAND_MAX );//随机变量在0到ArrayLength-1之间取值
x=SquareArray[RandNum].pt.x;
y=SquareArray[RandNum].pt.y;
}while(SquareArray[RandNum].HitFlag==TRUE);//检查小块以前是否被击中过,如果是,重新计算一个随机数
SquareArray[RandNum].HitFlag=TRUE;//设置击中标志
::StretchDIBits(pDC->GetSafeHdc(),
x, y, 12,12,
x,bitmapHeight-y-12, 12,12,
m_pImgData, pBitmapInfo,DIB_RGB_COLORS, SRCCOPY);
Sleep(1);//设置延时时间
}
delete [] SquareArray;
}
具体见:https://github.com/lvxiaojie111/VS2015CPlus-Dig.Img.Pro..git