用VB写高效的图像处理程序自盘古开天地以来(好像夸张了点),一直有人抱怨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;单位:毫秒Windows98WindowsXP说明VB_PSet1,199.4553786.1885在VB使用PSet画的VB_SetPixel872.3621451.3712在VB使用SetPixelV画的VB_DIB8.22188.2226在VB使用DIB画的VB_DIB_Ptr9.67839.4420在VB使用DIBSection+模拟指针画的VC(Debug)6.68966.6503VC写的(Debug版)VC(Release)3.27363.6247VC写的(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中写能够实时处理的图像处理程序。在Windows3.0以前,Windows系统用的是DDB(设备有关位图)。DDB没有调色板,显示的颜色依赖硬件,处理色彩很不方便。所以Microsoft在Windows3.0中重新定义了BMP文件格式(BMP3.0),使其支持设备无关位图——也就是DIB。时至今日,BMP的版本号已升至5.0(WindowsNT4.0、Windows95定义了BMP4.0,Windows98、Windows2000定义了BMP5.0),但基本结构没有变——仍是BMP文件头和DIB组成:BMP文件BITMAPFILEHEADERBMP文件头DIBBITMAPINFOHEADER位图信息头BITMAPINFORGBQUAD[]调色板位图数据(#代表可以不填(=0)的项目)BMP文件头——BITMAPFILEHEADER原型定义:typedefstructtagBITMAPFILEHEADER{//bmfhWORDbfType;DWORDbfSize;WORDbfReserved1;WORDbfReserved2;DWORDbfOffBits;}BITMAPFILEHEADER;VB声明:TypeBITMAPFILEHEADERbfType(0to1)AsBytebfSizeAsLongbfReserved1AsIntegerbfReserved2AsIntegerbfOffBitsAsLongEndType说明:bfType指示文件的类型,必须是“BM”bfSize#指示文件的大小,包括BITMAPFILEHEADERbfReserved1保留,=0bfReserved2保留,=0bfOffBits#从文件头到位图数据的偏移字节数文件信息头——BITMAPINFOHEADER原型定义:typedefstructtagBITMAPINFOHEADER{//bmihDWORDbiSize;LONGbiWidth;LONGbiHeight;WORDbiPlanes;WORDbiBitCount;DWORDbiCompression;DWORDbiSizeImage;LONGbiXPelsPerMeter;LONGbiYPelsPerMeter;DWORDbiClrUsed;DWORDbiClrImportant;}BITMAPINFOHEADER;VB声明:TypeBITMAPINFOHEADERbiSizeAsLongbiWidthAsLongbiHeightAsLongbiPlanesAsIntegerbiBitCountAsIntegerbiCompressionAsLongbiSizeImageAsLongbiXPelsPerMeterAsLongbiYPelsPerMeterAsLongbiClrUsedAsLongbiClrImportantAsLongEndType说明:biSizeBITMAPINFOHEADER结构的大小。BMP有多个版本,就靠biSize来区别:BMP3.0:BITMAPINFOHEADER(=40)BMP4.0:BITMAPV4HEADER(=108)BMP5.0:BITMAPV5HEADER(=124)biWidth位图的高度,单位是像素biHeight位图的宽度,单位是像素biPlanes设备的位平面数。现在都是1biBitCount图像的颜色位数0:当biCompression=BI_JPEG时必须为0(BMP5.0)1:单色位图4:16色位图8:256色位图16:增强色位图,默认为555格式24:真彩色位图32:32位位图,默认情况下Windows不会处理最高8位,可以将它作为自己的Alpha通道biCompression压缩方式BI_RGB:无压缩BI_RLE8:行程编码压缩,biBitCount必须等于8BI_RLE4:行程编码压缩,biBitCount必须等于4BI_BITFIELDS:指定RGB掩码,biBitCount必须等于16、32BI_JPEG:JPEG压缩(BMP5.0)BI_PNG:PNG压缩(BMP5.0)biSizeImage#实际的位图数据所占字节(biCompression=BI_RGB时可以省略)biXPelsPerMeter#目标设备的水平分辨率,单位是每米的像素个数biYPelsPerMeter#目标设备的垂直分辨率,单位是每米的像素个数biClrUsed#使用的颜色数(当biBitCount等于1、4、8时才有效)。如果该项为0,表示颜色数为2^biBitCountbiClrImportant#重要的颜色数。如果该项为0,表示所有颜色都是重要的调色板只有biBitCount等于1、4、8时才有调色板。调色板实际上是一个数组,元素的个数由biBitCount和biClrUsed决定。原型定义:typedefstructtagRGBQUAD{//rgbqBYTErgbBlue;BYTErgbGreen;BYTErgbRed;BYTErgbReserved;}RGBQUAD;VB声明:PrivateTypeRGBQUADrgbBlueAsBytergbGreenAsBytergbRedAsBytergbReservedAsByteEndType说明:rgbBlue蓝色分量rgbGreen绿色分量rgbRed红色分量rgbReserved#保留,=0位图数据◆扫描行:一行的图像数据叫做一个扫描行。一个扫描行的长度必须是4的倍数(字节),如果不是,则需要补齐。计算公式:LineBytes=((biWidth*biBitCount+31)And&HFFFFFFE0)\8由于BMP设定者认为数学坐标系更总要,所以DIB的扫描行是逆序存储的(相对于屏幕坐标系而言),即屏幕上的第一行是DIB位图数据的最后一行。◆1位色:用1位表示一个像素,所以一个字节可以表示8个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:字节0...位76543210像素01234567◆4位色:用4位表示一个像素,所以一个字节可以表示2个像素。坐标是从最左边(最高位)开始的,而不是一般情况下的最低位。在内存的摆放形式如下:字节0...位76543210像素01像素位32103210◆8位色:用8位表示一个像素,所以一个字节刚好只能表示一个像素。在内存的摆放形式如下:字节01...像素01◆16位色:用16位表示一个像素,所以两个字节可以表示1个像素。默认情况下16位DIB是555格式,最高位无效(这对VB是个福音,因为VB没有16位无符号型)。在内存的摆放形式如下(PC机是低字节在前):字节0123...位76543210765432107654321076543210像素01RGBGBxRGGBxRGRGB位21043210043210432104321004321043◆24位色:用24位表示一个像素,所以三个字节可以表示1个像素。注意它的顺序是BGR,而不是传统的RGB。在内存的摆放形式如下:字节012345...像素01RGBBGRBGR◆32位色:用32位表示一个像素,所以四个字节可以表示1个像素。注意绝大多数的GDI函数不会处理Alpha通道(只有AlphaBlend支持)。在内存的摆放形式如下:字节01234567...像素01RGBBGRABGRASetDIBitsToDevice原型定义:intSetDIBitsToDevice(HDChDC,//handletodevicecontextintXDest,//x-coordinateofupper-leftcornerofdest.rect.intYDest,//y-coordinateofupper-leftcornerofdest.rect.DWORDdwWidth,//sourcerectanglewidthDWORDdwHeight,//sourcerectangleheightintXSrc,//x-coordinateoflower-leftcornerofsourcerect.intYSrc,//y-coordinateoflower-leftcornerofsourcerect.UINTuStartScan,//firstscanlineinarrayUINTcScanLines,//numberofscanlinesCONSTVOID*lpvBits,//addressofarraywithDIBbitsCONSTBITMAPINFO*lpbmi,//addressofstructurewithbitmapinfo.UINTfuColorUse//RGBorpaletteindexes);VB声明:DeclareFunctionSetDIBitsToDeviceLibgdi32.dll(ByValhDCAsLong,ByValXDestAsLong,ByValYDestAsLong,ByValdwWidthAsLong,ByValdwHeightAsLong,ByValXSrcAsLong,ByValYSrcAsLong,ByValuStartScanAsLong,ByValcScanLinesAsLong,lpvBitsAsAn