前言
做一个lifegame的演示程序,lifegame是一个很有名的游戏,现在需要对其结果(二维bool数组)进行图形化演示。如果细胞存活(二维数组中的某一值为1),则对应黑色像素点,否则对应白色像素点。
例如,开局全部像素随机取黑或白,在经历过40次迭代后,其效果如下:
根据细胞的存活状态对图片的每一像素赋值是一个相当耗时的行为,本文将介绍MFC下的,基于openmp和CImage的解决思路。
openmp
vs2010中使用openmp的例子见Openmp。这篇博文中介绍了openmp的良好性能。
openmp的一个使用前提是对一段共享内存进行并行操作。在本文的例子中,首先需要新建一张图片,接着获取图片所在内存的指针,对其进行修改,最后进行图片的保存。
CImage新建图片并获取图片数据指针
CImage是一个图片处理类,新建一个对象后,可以通过其Create函数新建一张指定大小的图片。通过其Save保存这张图片。CImage本身是有修改图片相应位置像素值的函数SetPixelRGB,如果不考虑并行操作可以直接调用SetPixelRGB对每一个像素进行操作。但是不同线程同时调用image_t.SetPixelRGB会出问题,具体原因未知。
这里通过将CImage对象转换为BITMAP对象,获取图片数据区域的指针,直接对内存进行操作,可以进行并行加速。这里需要注意的是内存区的像素位置和图片中的有所不同,new_grid二维数组的下标对应到图片中为:图片左上角对应(0,0),右下角对应(imax-1,jmax-1)。这里imax和jmax是图片的大小。而内存中有所不同,见代码。
// 新建一个CImage对象
CImage image_t;
// 支持alpha通道
image_t.Create(imax, jmax, 32, 1);
// 将CImage对象转换为HBITMAP句柄
HBITMAP hmp = image_t;
// 从HBITMAP句柄中获取BITMAP结构体
BITMAP bmp;
GetObject(hmp, sizeof(BITMAP), (LPSTR)&bmp);
// 获取图片存储区域指针
UINT32* pdata = (UINT32*) bmp.bmBits;
{
#pragma omp parallel for
// 并行加速图片生成以及存活细胞数统计
for(int i = 0; i < imax; i++)
{
for (int j = 0; j < jmax; j++)
{
if(new_grid[i][jmax-j-1])
{
// 黑色
*(pdata + i + j*imax) = 0;
//image_t.SetPixelRGB(i, j, 0, 0, 0);
totalLivingCeils++;
}
else
{
// 白色
*(pdata + i + j*imax) = 0xffffff;
//image_t.SetPixelRGB(i, j, 255, 255, 255);
}
}
}
}
// 将片保存到磁盘
CString filename;
filename.Format(_T("res_%d.jpg"), 0);
image_t.Save(filename);