一、为什么这么慢? 自盘古开天地以来(好像夸张了点),一直有人抱怨VB程序速度慢。特别是图像处理,被认为是VB的禁区。说起来也是,市面上的关于VB的图像处理的数据都是先讲计算公式,再直接用PSet(或API函数SetPixel)逐点画(至少我见过的书都是这样)。效果是办到了,但速度慢得离谱:对一幅640*480的图像进行半透明合并就需要10秒钟;而在PhotoShop中,只要一设置图层的透明度,半透明效果立即呈现。难怪有人说VB的闲话。
但这并不表示VB不能写高速的图像处理程序,速度慢是因为没有使用正确的方法。
从VB5开始,能以本机代码编译成exe文件,所以不存在代码执行速度的问题。那么,是什么拖慢了速度呢?就是PSet和SetPixel!PSet把浮点形式的坐标转为像素单位,再交给SetPixel处理。而SetPixel呢,坐标系转化、剪裁区域判断、将颜色匹配为设备支持的最接近的,最后还要根据不同的颜色格式寻址、为将颜色写入其所在位进行位运算。经过这么多层处理,速度不慢才怪。
那么,怎样才能提高处理速度呢?使用DIB,直接对位图所在内存进行操作,速度可以大大提高。现在看看范例程序,这只是一个简单的色彩演示程序。
CPU:赛扬333;内存:PC100(很老的概念了)的SDRAM,128MB;单位:毫秒
Windows 98
Windows XP
说 明
VB_PSet
1,199.4553
786.1885
在VB使用 PSet 画的
VB_SetPixel
872.3621
451.3712
在VB使用 SetPixelV 画的
VB_DIB
8.2218
8.2226
在VB使用 DIB 画的
VB_DIB_Ptr
9.6783
9.4420
在VB使用 DIBSection + 模拟指针 画的
VC(Debug)
6.6896
6.6503
VC写的(Debug版)
VC(Release)
3.2736
3.6247
VC写的(Release版) 从这个表中可看出: 1.VC比VB_DIB、VB_DIB_Ptr快两倍,这是因为SafeArray结构的数组比真正的指针慢,但也不是某些人所说的70~100倍; 2.VB_DIB_Ptr比VB_DIB慢一点,这是因为模拟指针本来就是靠SafeArray结构的数组,而且模拟指针需要对两个数组进行操作,所以速度慢一点; 3.真正差了70~100倍是VB_PSet和VB_SetPixel,特别是VB_PSet在Windows98下与VB_DIB差了145倍。
以上可证,速度慢的原因是SetPixel非常低效,而并不是VB的问题。虽然VC的的确比较快,但是我写这篇文章不是为了讨论速度极限(否则这篇文章会改名为《如何用汇编写高速的图像处理程序》),而是为了告诉大家如何在VB中写能够实时处理的图像处理程序。
二、DIB的结构 在 Windows 3.0 以前,Windows系统用的是DDB(设备有关位图)。DDB没有调色板,显示的颜色依赖硬件,处理色彩很不方便。所以 Microsoft 在 Windows 3.0中 重新定义了BMP文件格式(BMP 3.0),使其支持设备无关位图——也就是DIB。
时至今日,BMP的版本号已升至5.0(Windows NT 4.0、Windows95 定义了 BMP 4.0,Windows 98、Windows 2000 定义了 BMP 5.0),但基本结构没有变——仍是 BMP文件头 和 DIB 组成:
BMP文件
BITMAPFILEHEADER
BMP文件头
DIB
BITMAPINFOHEADER
位图信息头
BITMAPINFO
RGBQUAD[]
调色板
位图数据 (#代表可以不填(=0)的项目)
BMP文件头——BITMAPFILEHEADER
原型定义:
typedef struct tagBITMAPFILEHEADER { // bmfh
WORD bfType;
DWORD bfSize;
WORD bfReserved1;
WORD bfReserved2;
DWORD bfOffBits;
} BITMAPFILEHEADER;
VB声明:
Type BITMAPFILEHEADER
bfType(0 to 1) As Byte
bfSize As Long
bfReserved1 As Integer
bfReserved2 As Integer
bfOffBits As Long
End Type
说明:
bfType
指示文件的类型,必须是“BM”
bfSize#
指示文件的大小,包括BITMAPFILEHEADER
bfReserved1
保留,=0
bfReserved2
保留,=0
bfOffBits#
从文件头到位图数据的偏移字节数
文件信息头——BITMAPINFOHEADER
原型定义:
typedef struct tagBITMAPINFOHEADER{ // bmih
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER;
VB声明:
Type BITMAPINFOHEADER
biSize As Long
biWidth As Long
biHeight As Long
biPlanes As Integer
biBitCount As Integer
biCompression As Long
biSizeImage As Long
biXPelsPerMeter As Long
biYPelsPerMeter As Long
biClrUsed As Long
biClrImportant As Long
End Type
说明:
biSize
BITMAPINFOHEADER结构的大小。BMP有多个版本,就靠biSize来区别: BMP3.0:BITMAPINFOHEADER(=40) BMP4.0:BITMAPV4HEADER(=108) BMP5.0:BITMAPV5HEADER(=124)
biWidth
位图的高度,单位是像素
biHeight
位图的宽度,单位是像素
biPlanes
设备的位平面数。现在都是1
biBitCount
图像的颜色位数 0:当biCompression=BI_JPEG时必须为0(BMP 5.0) 1:单色位图 4:16色位图 8:256色位图 16:增强色位图,默认为555格式 24:真彩色位图 32:32位位图,默认情况下Windows不会处理最高8位,可以将它作为自己的Alpha通道
biCompression
压缩方式 BI_RGB:无压缩 BI_RLE8:行程编码压缩,biBitCount必须等于8 BI_RLE4:行程编码压缩,biBitCount必须等于4 BI_BITFIELDS:指定RGB掩码,biBitCount必须等于16、32 BI_JPEG:JPEG压缩(BMP 5.0) BI_PNG:PNG压缩(BMP 5.0)
biSizeImage#
实际的位图数据所占字节(biCompression=BI_RGB时可以省略)
biXPelsPerMeter#
目标设备的水平分辨率,单位是每米的像素个数
biYPelsPerMeter#
目标设备的垂直分辨率,单位是每米的像素个数
biClrUsed#
使用的颜色数(当biBitCount等于1、4、8时才有效)。如果该项为0,表示颜色数为2^biBitCount
biClrImportant#
重要的颜色数。如果该项为0,表示所有颜色都是重要的
调色板 只有biBitCount等于1、4、8时才有调色板。调色板实际上是一个数组,元素的个数由biBitCount和biClrUsed决定。
原型定义:
typedef struct tagRGBQUAD { // rgbq
BYTE rgbBlue;
BYTE rgbGreen;
BYTE rgbRed;
BYTE rgbReserved;
} RGBQUAD;
VB声明:
Private Type RGBQUAD
rgbBlue As Byte
rgbGreen As Byte
rgbRed As Byte
rgbReserved As Byte
End Type
说明:
rgbBlue
蓝色分量
rgbGreen
绿色分量
rgbRed
红色分量
rgbReserved#
保留,=0
位图数据
◆扫描行: 一行的图像数据叫做一个扫描行。一个扫描行的长度必须是4的倍数(字节),如果不是,则需要补齐。计算公式:LineBytes=((biWidth*biBitCount+31)And &HFFFFFFE0)\8 由于BMP设定者认为数学坐标系更总要,所以DIB的扫描行是逆序存储的(相对于屏幕坐标系而言),即屏幕上的第一行是DIB位图数据的最后一行。
◆1位色: 用1位表示一个像素,所以一个字节可以表示8个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
字节
0
...
位
7
6
5
4
3
2
1
0
像素
0
1
2
3
4
5
6
7
◆4位色: 用4位表示一个像素,所以一个字节可以表示2个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:
字节
0
...
位
7
6
5
4
3
2
1
0
像素
0
1
像素位
3
2
1
0
3
2
1
0
◆8位色: 用8位表示一个像素,所以一个字节刚好只能表示一个像素。在内存的摆放形式如下:
字节
0
1
...
像素
0
1
◆16位色: 用16位表示一个像素,所以两个字节可以表示1个像素。默认情况下16位DIB是555格式,最高位无效(这对VB是个福音,因为VB没有16位无符号型)。在内存的摆放形式如下(PC机是低字节在前):
字节
0
1
2
3
...
位
7
6
5
4
3
2
1
0
7
6
5
4
3
2
1
0
7
6
5
4
3
2
1
0
7
6
5
4
3
2
1
0
像素
0
1
RGB
G
B
x
R
G
G
B
x
R
G
RGB位
2
1
0
4
3
2
1
0
0
4
3
2
1
0
4
3
2
1
0
4
3
2
1
0
0
4
3
2
1
0
4
3
◆24位色: 用24位表示一个像素,所以三个字节可以表示1个像素。注意它的顺序是BGR,而不是传统的RGB。在内存的摆放形式如下:
字节
0
1
2
3
4
5
...
像素
0
1
RGB
B
G
R
B
G
R
◆32位色: 用32位表示一个像素,所以四个字节可以表示1个像素。注意绝大多数的GDI函数不会处理Alpha通道(只有AlphaBlend支持)。在内存的摆放形式如下:
字节
0
1
2
3
4
5
6
7
...
像素
0
1
RGB
B
G
R
A
B
G
R
A
三、DIB访问函数
SetDIBitsToDevice
原型定义:
int SetDIBitsToDevice(
HDC hDC, // handle to device context
int XDest, // x-coordinate of upper-left corner of dest. rect.
int YDest, // y-coordinate of upper-left corner of dest. rect.
DWORD dwWidth, // source rectangle width
DWORD dwHeight, // source rectangle height
int XSrc, // x-coordinate of lower-left corner of source rect.
int YSrc, // y-coordinate of lower-left corner of source rect.
UINT uStartScan, // first scan line in array
UINT cScanLines, // number of scan lines
CONST VOID *lpvBits, // address of array with DIB bits
CONST BITMAPINFO *lpbmi, // address of structure with bitmap info.
UINT fuColorUse // RGB or palette indexes
);
VB声明:
Declare Function SetDIBitsToDevice Lib "gdi32.dll" (ByVal hDC As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal dwWidth As Long, ByVal dwHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As Any, lpbmi As Any, ByVal fuColorUse As Long) As Long
说明:
将一幅与设备无关位图的全部或部分数据直接复制到一个设备。这个函数在设备中定义了一个目标矩形,以便接收位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据
返回值:
如函数执行成功,返回欲复制的扫描线的数量;如返回常数GDI_ERROR,表示出错
参数:
hDC
一个设备场景的句柄。该场景用于接收位图数据
XDest
指定绘制区域的左上角X坐标
YDest
指定绘制区域的左上角Y坐标
dwWidth
指定绘制区域的高度
dwHeight
指定绘制区域的宽度
XSrc
矩形在DIB中的起点X坐标
YSrc
矩形在DIB中的起点Y坐标
uStartScan
lpvBits中第一条扫描线的编号。如lpbmi之BITMAPINFOHEADER部分的biHeight字段是正数,那么这条扫描线就会从位图的底部开始计算;如果是负数,就从顶部开始计算
cScanLines
欲复制的扫描线数量
lpvBits
指向一个缓冲区的指针。这个缓冲区包含了以DIB格式描述的位图数据;这种格式是由lpbmi指定的
lpbmi[1] [2] [3] 下一页 没有相关教程
|