(翻译了个大概,略过了一些无关紧要的句子,原文)
使用API调用是一种比使用VB内置图像函数更快速的方法. 例如,使用GetPixel和SetPixel要比VB的PSet和Point快3倍左右. 但是如果使用直接内存访问(DMA)将得到更高的效率.
那么,用DirectX怎么样? 当然DirectX是相当快的,但它的速度来自于显卡,如果你不幸地没有那么一块好显卡,或者你只是想处理简单的图像,(比如画点和线),那么DMA已经够快的了,并且能把你从麻烦的DirectX版本中解放出来.
基本思想就是直接访问图像组成, 你可能会想到使用GetDibits和SetDibits去费力地把图像从位图提取到数组,再把数组塞进图像. 而实际上,你可以将一个数组直接指向位图的内存.
要实现这样的数组当然需要用到一些API,见下:
使用VarPtr函数可以得到一个变量的内存地址,在此,我也引用了一个可以指向数组的函数VarPtrArray.
使用CopyMemory则可以将数据从一块内存复制到另一块内存.
使用GetObj则可以获得一个对象的内存,在例子中我们用它来得到StdPicture对象的信息.因此,我们需要定义一个位图结构.
最后我们定义了一个安全数组结构,我们用它来替换实际工作的数组.
我们先定义StdPicture对象,并且假设它已经加载了一个图像. 再定义一个动态数组而不初始化. 我们使用API来将这个数组"分配"到图像的内存,这样,我们在数组上的任何改动就能显示在图像上了. 代码:
看看发生了什么? 首先我们用GetObj来获取了位图的信息,然后使用这个信息来构造了SafeArray2d结构,特别注意这句:
.pvdata = bmp.bmbits
它将位图位图所在的内存指向了结构.
简单把, 不尽然,这里有些需要注意的地方:
1:图像对象必须预先载入一个图片才能建立一个位图结构,否则它是没有意义的.
2:安全数组对象必须和数组的生存周期一致,如果你在数组被回收之前就释放了安全数组对象,那么数组将无处可指(导致VB崩溃)
3:而在释放数组之前,又必须将数组还原,否则程序会因为内存泄漏而崩溃.
4:一个256色的位图中,每个像素占用1个字节,但是这个字节只是对应了一个调色板索引而并非一个实际的颜色值. 所以你必须在建立数组之前先把索引转换成时机颜色.(这里不讨论16位色) 幸运的是24位色图像中存放的是真正的颜色值,但你依然需要颜色对应到RGB字节中
5:通常来说,位图对应的数组是从左下角开始的,因此数组(0,0)对应图像的最左下角的点
在你弄完之后,你必须复位数组,见下:
那么现在这个数组是什么呢?它变成了一个2维数组(X,Y),X代表横坐标,Y代表纵坐标,每一个数组元素是一个字节. 对于256色位图来说,这个字节是一个颜色索引,但对于24位色图像来说,它是一个颜色值. 每个字节对应了颜色值中的RGB中的一个,所以每个像素是有3个个数组元素组成的.例如:一个100X100的图像对应一个300X100大小的数组.
数组的大小相对于像素是不同的,因此你当你读写某个像素的时候必须自己计算:
这里给出一个速度对比:
加载一个100X100的24位图像, 我把每个像素都设置为红色,使用DMA,SetPixel和PSet时间分别为:5ms,60ms,550ms. 编译之后,分别为:4ms,50ms,70ms. DMA比SetPixel快12倍
附上封装好的模块, 只要把它编译为ActiveX DLL然后就可以在你自己的工程中引用它.
下面是我的测试工程,演示了使用方法: