用VB写高效的图像处理程序www.27111.com

用VB写高效的图像处理程序 V2.0(2006-5-24)

作者:zyl910

一、为什么这么慢?
二、DIB的结构
三、DIB访问函数
四、实战练习
五、使用DIBSection和模拟指针
六、结合DirectX

一、为何那样慢?

  自盘古真人开天地以来(好像夸张了点),一贯有人抱怨VB程序速度慢。尤其是图像处理,被认为是VB的禁区。说起来也是,市面上的有关VB的图像处理的数码都是先讲统计公式,再一向用PSet(或API函数SetPixel)逐点画(至少我见过的书都是那样)。效果是办到了,但速度慢得不可相信:对一幅640*480的图像进行半透明合并就须要10分钟;而在PhotoShop中,只要一设置图层的透明度,半透明效果立时显现。难怪有人说VB的拉扯。

 

  但这并不表示VB无法写高速的图像处理程序,速度慢是因为从没利用正确的章程。

 

  从VB5起头,能以本机代码编译成exe文件,所以不设有代码执行速度的标题。那么,是怎么拖慢了进程吗?就是PSet和SetPixel!PSet把浮点格局的坐标转为像素单位,再提交SetPixel处理。而SetPixel呢,坐标系转化、剪裁区域判断、将颜色相配为设备援助的最相近的,最终还要按照不一样的颜色格式寻址、为将颜色写入其所在位举办位运算。经过如此多层处理,速度不慢才怪。

 

  那么,如何才能升高处理速度呢?使用DIB,直接对位图所在内存进行操作,速度可以大大进步。现在探视本文提供的范例程序的推行进程,那只是一个简便的情调演示程序。

 

图像尺寸:640*480*24b。单位:毫秒
  Debug Relase 说 明
1_PSet 1156.7042 936.2807 在VB使用 PSet 画的
2_SetPixelV 484.7545 460.5382 在VB使用 SetPixelV 画的
3_DIB 118.6978 3.8317 在VB使用 DIB 画的
4_DIB_Ptr 107.5791 4.3545 在VB使用 DirectDraw + 模拟指针 画的
5_DX7Ptr 108.1261 4.5503 在VB使用 DirectDraw + 模拟指针 画的
6_DX7Arr 131.8148 7.5506 在VB使用 DIBSection + GetLockedArray 画的
VC 2.8535 1.8994 用Visual C++ 6.0写的
我的电脑配置
CPU AMD Athlon XP 1700+(实际频率:1463 MHz (11 x 133))
内存 Kingston DDR266 256MB *2(两根)
显卡 nVIDIA GeForce2 MX/MX 400(AGP 4X,显存32MB)
测试环境 Windows XP sp2

www.27111.com 1
  从这些表中可看出:
  1.VC比3_DIB、4_DIB_Ptr快一些,那是因为SafeArray结构的数组比真正的指针慢,但也不是某些人所说的70~100倍;
  2.4_DIB_Ptr比3_DIB慢一点,那是因为模仿指针本来就是靠SafeArray结构的数组,必要在运行时动态修改数组数据地址,所以速度慢一点;
  3.当真差了70~100倍是1_PSet和2_SetPixelV。
  4.在VB
IDE中表达实施顺序极度慢,3_DIB就存在30倍的快慢差异。所以日常一些世俗的人拿
VC Debug编译的顺序 与 VB IDE中解释施行顺序 比较速度。
  5.4_DIB_www.27111.com,Ptr、5_DX7Ptr速度一样,这是因为都是采纳模拟指针技术一贯访问内存来做图像处理的。而6_DX7Arr使用的是GetLockedArray再次回到的二维数组,二维数组比一维数组慢。

 

  以上可证,速度慢的原由是SetPixelV极度低效,而并不是VB的难点。就算VC的的确比较快,然而本人写那篇小说不是为着商量速度极限(否则那篇作品会改名为《如何用汇编写高速的图像处理程序》),而是为了告诉大家怎么在VB中写可以实时处理的图像处理程序。

 

  同时,决定代码速度的不是编程语言或编译器,而是算法。假设算法写得差的话,无论你用哪些编程语言或编译器,那程序速度照旧很慢(你在VC中用SetPixelV写图像处理程序试试)。而近年来的硬件配备已经足足好,用VB完全可以写高速的图像处理程序。

 

二、DIB的结构

  在 Windows 3.0
在此从前,Windows系统用的是DDB(设备有关位图)。DDB没有调色板,突显的颜色重视硬件,处理色彩很不便民。所以
Microsoft 在 Windows 3.0中 重新定义了BMP文件格式(Bmp4.0),使其匡助设备毫无干系位图——也就是DIB。

 

  时至前天,BMP的版本号已升至5.0(Windows NT 4.0、Windows95 定义了 BMP3.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机使用小端规则(little
endian),是低字节在前):

字节

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

指向BITMAPINFO(为包容Bmp5/5而表明成Any),对DIB的格式和颜色举办描述的一个协会

fuColorUse

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

StretchDIBits

原型定义:

int StretchDIBits(
  HDC hDC,                // handle to device context
  int XDest,              // x-coordinate of upper-left corner of dest. rectangle
  int YDest,              // y-coordinate of upper-left corner of dest. rectangle
  int nDestWidth,         // width of destination rectangle
  int nDestHeight,        // height of destination rectangle
  int XSrc,               // x-coordinate of upper-left corner of source rectangle
  int YSrc,               // y-coordinate of upper-left corner of source rectangle
  int nSrcWidth,          // width of source rectangle
  int nSrcHeight,         // height of source rectangle
  CONST VOID *lpBits,            // address of bitmap bits
  CONST BITMAPINFO *lpBitsInfo,  // address of bitmap data
  UINT iUsage,                   // usage flags
  DWORD dwRop                    // raster operation code
);

VB声明:

Declare Function StretchDIBits Lib “gdi32” (ByVal hDC As Long, ByVal
XDest As Long, ByVal YDest As Long, ByVal nDestWidth As Long, ByVal
nDestHeight As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal
nSrcWidth As Long, ByVal nSrcHeight As Long, lpBits As Any, lpBitsInfo
As Any, ByVal wUsage As Long, ByVal dwRop As Long) As Long

说明:

按照一幅与设备无关的位图创建一幅与装备有关的位图

返回值:

履行成功重回位图句柄,零意味着失利

参数:

hDC

一个装置场景的句柄,该设备场景定义了要创制的与设施有关位图的安插新闻

XDest

指定绘制区域的左上角X坐标

YDest

指定绘制区域的左上角Y坐标

nDestWidth

点名绘制区域的万丈

nDestHeight

点名绘制区域的宽窄

XSrc

矩形在DIB中的源点X坐标

YSrc

矩形在DIB中的源点Y坐标

nSrcWidth

点名原位图绘制区域的左上角X坐标

nSrcHeight

指定原位图绘制区域的左上角Y坐标

lpBits

针对一个缓冲区的指针。那一个缓冲区包蕴了以DIB格式描述的位图数据;这种格式是由lpBitsInfo指定的

lpBitsInfo

指向BITMAPINFO(为包容BVCD/5而讲明成Any),对DIB的格式和颜料举行描述的一个结构

iUsage

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

dwRop

欲进行的光栅运算

CreateDIBitmap

原型定义:

HBITMAP CreateDIBitmap(
  HDC hDC,                  // handle to device context
  CONST BITMAPINFOHEADER *lpbmih,  // pointer to bitmap size and format data
  DWORD fdwInit,            // initialization flag
  CONST VOID *lpbInit,      // pointer to initialization data
  CONST BITMAPINFO *lpbmi,  // pointer to bitmap color-format data
  UINT fuUsage              // color-data usage
);

VB声明:

Declare Function CreateDIBitmap Lib “gdi32” (ByVal hDC As Long, lpbmih
As Any, ByVal fdwInit As Long, lpbInit As Any, lpbmi As Any, ByVal
fuUsage As Long) As Long

说明:

将一幅与设施无关位图的整个或部分数据直接复制到一个装置。这几个函数在装置中定义了一个目标矩形,以便接受位图数据。它也在DIB中定义了一个源矩形,以便从中提取数据

返回值:

实践成功则赶回扫描线的数量,零意味败北。会设置GetLastError

参数:

hDC

一个装备场景的句柄。该现象用于吸纳位图数据

lpbmih

BITMAPINFOHEADER(为包容BMP3/5而讲明成Any),对DIB的格式进行描述的一个布局

fdwInit

如不应对位图数据举行开始化,那么设为零。如设为CBM_INIT,表示按照lpbInit和
lpbmi参数对位图举行初叶化

lpbInit

本着一个缓冲区的指针。那一个缓冲区包罗了以DIB格式描述的位图数据;那种格式是由lpbmi指定的

lpbmi

指向BITMAPINFO(为包容BMP3/5而评释成Any),对DIB的格式和颜料进行描述的一个构造

fuUsage

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

CreateDIBSection

原型定义:

HBITMAP CreateDIBSection(
  HDC hDC,          // handle to device context
  CONST BITMAPINFO *lpbmi,
                    // pointer to structure containing bitmap size, format, and color data
  UINT iUsage,      // color data type indicator: RGB values or palette indexes
  VOID *ppvBits,    // pointer to variable to receive a pointer to  the bitmap's bit values
  HANDLE hSection,  // optional handle to a file mapping object
  DWORD dwOffset    // offset to the bitmap bit values within the file mapping object
);

VB声明:

Declare Function CreateDIBSection Lib “gdi32” (ByVal hDC As Long, lpbmi
As Any, ByVal iUsage As Long, ByRef ppvBits As Long, ByVal hSection As
Long, ByVal dwOffset As Long) As Long

说明:

CreateDIBSection能创造一种特殊的DIB,称为DIB项(DIBSection),然后重返一个GDI位图的句柄。它提供了DIB和GDI位图的最好的特征。那样我们得以从来访问DIB的内存,可以使用位图句柄和内存设备条件,大家居然还足以在DIB中调用GDI函数来绘图

返回值:

推行成功重回DIBSection位图的句柄,零意味着败北。会设置GetLastError

参数:

hDC

一个装置场景的句柄。如dw设为DIB_PAL_COLORS,那么DIB颜色表就会用来自逻辑调色板的颜色举办初阶化

lpbmi

指向BITMAPINFO(为兼容BMP5/5而评释成Any),这几个布局开端化成欲创立的那幅位图的布署数据

iUsage

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

ppvBits

用以获取DIBSection数据区的内存地址

hSection

针对一个文书映射对象的可选句柄,位图将在里边创建。如设为零,Windows会自动分配内存

dwOffset

如指定了句柄,就用这一个参数指定位图数据在文书映射对象中的偏移量

GetDIBits

原型定义:

int GetDIBits(
  HDC hDC,           // handle to device context
  HBITMAP hbmp,      // handle to bitmap
  UINT uStartScan,   // first scan line to set in destination bitmap
  UINT cScanLines,   // number of scan lines to copy
  LPVOID lpvBits,    // address of array for bitmap bits
  LPBITMAPINFO lpbmi,// address of structure with bitmap data
  UINT uUsage        // RGB or palette index
);

VB声明:

Declare Function GetDIBits Lib “gdi32” (ByVal hDC As Long, ByVal hbmp As
Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As
Any, lpbmi As Any, ByVal uUsage As Long) As Long

说明:

该函数利用申请到的内存,由GDI位图拿到DIB位图数据。通过该函数,可以对DIB的格式进行控制,可以制定颜色的位数,而且可以指定是或不是开展削减。要是应用了压缩形式,则必须调用该函数五次,一回为了拿走所需内存,其余五回为了取得位图数据

返回值:

执行成功则赶回扫描线的数额,零意味战败。会安装GetLastError

参数:

hDC

概念了与装备有关位图hBitmap的布署新闻的一个设施场景的句柄

hbmp

源位图的句柄

uStartScan

欲复制到DIB中的第一条扫描线的编号

cScanLines

欲复制的扫描线数量

lpvBits

针对一个缓冲区的指针。那一个缓冲区包含了以DIB格式描述的位图数据;那种格式是由lpbmi指定的

lpbmi

指向BITMAPINFO(为包容Bmp3/5而表明成Any).对DIB的格式及颜色举办表达的一个构造。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的所有字段都无法不开端化

uUsage

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

SetDIBits

原型定义:

int SetDIBits(
  HDC hDC,                  // handle to device context
  HBITMAP hbmp,             // handle to bitmap
  UINT uStartScan,          // starting scan line
  UINT cScanLines,          // number of scan lines
  CONST VOID *lpvBits,      // array of bitmap bits
  CONST BITMAPINFO *lpbmi,  // address of structure with bitmap data
  UINT uUsage               // type of color indexes to use
);

VB声明:

Declare Function SetDIBits Lib “gdi32” (ByVal hDC As Long, ByVal hbmp As
Long, ByVal uStartScan As Long, ByVal cScanLines As Long, lpvBits As
Any, lpbmi As Any, ByVal uUsage As Long) As Long

说明:

未来自与装备毫不相关位图的二进制位复制到一幅与设施有关的位图里

返回值:

履行成功则赶回扫描线的数额,零代表退步。会安装GetLastError

参数:

hDC

概念了与设施有关位图hBitmap的布置信息的一个配备场景的句柄

hbmp

源位图的句柄

uStartScan

欲复制到DIB中的第一条扫描线的数码

cScanLines

欲复制的扫描线数量

lpvBits

针对一个缓冲区的指针。这些缓冲区包括了以DIB格式描述的位图数据;那种格式是由lpbmi指定的

lpbmi

指向BITMAPINFO(为包容Bmp4/5而注解成Any).对DIB的格式及颜色举行求证的一个协会。在BITMAPINFOHEADER结构中,从biSize到biCompression之间的持有字段都必须初阶化

uUsage

DIB_PAL_COLORS 颜色表是一个整数数组,其中包含了与目前选入hDC设备场景的调色板相关的索引
DIB_RGB_COLORS 颜色表包含了RGB颜色

GetDIBColorTable

原型定义:

UINT GetDIBColorTable(
  HDC hDC,          // handle to device context whose DIB is of interest
  UINT uStartIndex, // color table index of first entry to retrieve
  UINT cEntries,    // number of color table entries to retrieve
  RGBQUAD *pColors  // pointer to buffer that receives color table entries
);

VB声明:

Declare Function GetDIBColorTable Lib “gdi32” (ByVal hDC As Long, ByVal
uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long

说明:

从选入设备场景的DIBSection中取得颜色表消息

返回值:

取回的水彩条目数量,零表示失利。会设置GetLastError

参数:

hDC

已选入了一个DIBSection对象的装备场景

uStartIndex

颜色表中欲取回的率先个条文的目录

cEntries

欲取回的条规数量

pColors

本条布局数组用于装载颜色表音信的首先个条款

SetDIBColorTable

原型定义:

UINT SetDIBColorTable(
  HDC hDC,                // handle to device context whose DIB is of interest
  UINT uStartIndex,       // color table index of first entry to set
  UINT cEntries,          // number of color table entries to set
  CONST RGBQUAD *pColors  // pointer to array of color table entries
);

VB声明:

Declare Function SetDIBColorTable Lib “gdi32” (ByVal hDC As Long, ByVal
uStartIndex As Long, ByVal cEntries As Long, pColors As RGBQUAD) As Long

说明:

从选入设备场景的DIBSection中获得颜色表音信

返回值:

取回的水彩条目数量,零表示战败。会安装GetLastError

参数:

hDC

已选入了一个DIBSection对象的装备场景

uStartIndex

颜色表中欲取回的率先个条文的目录

cEntries

欲取回的条规数量

pColors

以此布局数组用于装载颜色表消息的首先个条款

四、实战演习

  用DIB写图像处理程序的时候,首先要明显一点:DIB并不是图像处理算法,而是一种绘图方法。图像处理算法是DIB高级,管理坐标和颜色的演算;而DIB只是为着绘制。所以这时拍卖算法的功能是速度的紧要性。

 

  利用DIB绘制图像并从未比用PSet/SetPixel绘制差多少,它只是把坐标运算改成地方运算而已。很几人了解指针是一个摇摇欲坠的东西,就是因为它能直接访问内存,假使指针不小心指错地方的话,Windows立刻告知一般爱护性错误。所以,在地点运算的时候肯定要小心,同时要专注每日保存,因为那时的不合法操作的暴发率相当高,否则辛劳顿苦写的代码一弹指间没了可别怪我没提示啊。

 

  好了,现在启幕!

 

  由于拍卖算法起辅导意义,所以现在先讲解1_PSet。所有的代码都在FrmMain.frm中。其余的历程的代码可以不看,现在将注意力集中在“DrawIt”中,它就是管绘制的。

 

vb6/1_PSet/FrmMain.frm中DrawIt
'绘制
Private Sub DrawIt()
    Dim I As Long, J As Long
    Static K As Long

    For I = 0 To ImgHeight - 1 'Y
        For J = 0 To ImgWidth - 1 'X
            PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
        Next J
    Next I

    K = (K + 1) And &HFF

End Sub

  其实自己那个演示程序蛮简单的:R分量延着水平方向伸张,G分量延着垂直方向伸张,B分量则从右往左滚动。什么?!“And
&HFF”是什么样看头?!那只是基础啊……(下略&HFFFF…字)。“&H”表示十六进制数,而And表示按位与。&HFF是二进制的“1111
1111”,正好覆盖了低8位,那时用And举行按位与,只会获得低8位,与RGB分量必要的8位刚刚吻合(对于“(J

  • K) And &HFF”来说,可以兑现滚动效应)。

 

  If Not 看领会了 Then Goto 前两段

 

  好!现在开拓3_DIB。(由于24位能一向指定RGB分量,所以那边是用的是24位DIB)

 

  看了前方的“DIB的结构”,是还是不是有点昏呢?其实DIB也没怎么,就是
一个表达位图新闻的BITMAPINFO结构 和
一个存储位图数据的多寡缓冲区,顶多再用SetDIBitsToDevice绘制,所以3_DIB与1_PSet比较只是多了SetDIBitsToDevice、BITMAPINFOHEADER(24位DIB没有调色板,所以用BITMAPINFOHEADER就行)和一部分常数的宣示而已。由于这几个演示程序不需求变更图像大小和色深,所以可以把有关变量作为窗体级变量,再在Form_Load中初始化。由于DIB并不曾向系统报名资源(数组的内存是VB分配的,会自动释放),所以不要求写释放代码。

 

  现在来看DrawIt。

vb6/3_DIB/FrmMain.frm中DrawIt
'绘制
Private Sub DrawIt()
    Dim I As Long, J As Long
    Static K As Long
    Dim iLinePtr As Long, iCurPtr As Long

    iLinePtr = (m_BI.biHeight - 1) * m_LineBytes 'DIB是逆序存储的
    For I = 0 To ImgHeight - 1 'Y
        iCurPtr = iLinePtr
        For J = 0 To ImgWidth - 1 'X
            'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
            m_MapData(iCurPtr + 2) = J And &HFF       'Red
            m_MapData(iCurPtr + 1) = I And &HFF       'Green
            m_MapData(iCurPtr + 0) = (J + K) And &HFF 'Blue
            iCurPtr = iCurPtr + 3 '24位
        Next J
        iLinePtr = iLinePtr - m_LineBytes
    Next I

    K = (K + 1) And &HFF

End Sub

  1.就算可以逐点把坐标映射成地址再写,可是这么功用太低了,可以使用坐标处理的一连性举办优化。
  2.是因为我那边用的是数组,所以那里用(数组元素)索引代替地址。
  3.最开端要专注DIB是逆序存储的,要将索引设为终极一行第三个像素的目录。
  4.由于DIB的RGB顺序是B、G、R,所以“m_MapData(CurIdx +
?)”的逐一是2、1、0。
  5.设置好一个像素的水彩后,要小心把索引改为下个像素的目录。
  6.是因为DIB是逆序存储的,移到下一个扫描行是“iLinePtr = iLinePtr –
m_LineBytes”

 


  “好了,代码看懂了,按F5运作看看效果。”
  “咦?速度好像没快多少呀?”


  那是由于程序在VB环境下是以表明情势运行的,而解释格局对做图像处理所须要广大循环和大气的算术运算的举办成效很低,所以要编译成(本机代码)exe再运行。此时还要小心编译优化,可以把“高级优化”的有所勾打上,速度可升级20%左右。

 

五、使用DIBSection和效仿指针

  就算有GetDIBits/SetDIBits函数,但是DIB与GDI位图之间的数据交流依然很不便利,尤其处理进程中必要调用GDI函数来处理的时候。而且就是你即便麻烦,但诸如此类做的处理效能很低。所以Windows为大家提供了DIBSection。DIBSection是一种特殊的DIB,它除了能够像DIB一样直接对位图数据所占内存举行操作,它还足以选入DC、能用GDI函数绘制,极度灵活。但在VB下使用DIBSection如故有困难的,因为用CreateDIBSection创立DIBSection时,获得的是位图数据的地方,而VB没有指针。

 

  所幸在VB下得以选拔SafeArray结构的数组模拟指针。关于模拟指针的规律、方法,网上的材料多的是,比如AdamBear的文章“VB真是想不到鳞次栉比之四:VB指针葵花宝典之SafeArray”。但那一个小说都只是讲一般性的应用,不可以像真的的指针一样自由改变地址(他们都是使用CopyMemory改的)。而在图像处理中,由于点运算的一再,“像真正的指针一样随便改动地址”的意义很关键。

 

  其实“像真正的指针一样随便更改地址”并从未技术难度(对于早已学会模拟指针的人来说),就看想获得不:直接将一个动态数组(设pByte)指向一个SAFEARRAY结构体变量(设pBytePtr)。模拟指针模块的代码在mPoint.bas

 

  现在来看4_DIB_Ptr:

vb6/4_DIB_Ptr/FrmMain.frm中DrawIt
'绘制
Private Sub DrawIt()
    Dim I As Long, J As Long
    Static K As Long
    Dim pByte() As Byte, pBytePtr As SAFEARRAY1D
    Dim iLinePtr As Long

    ' check Image
    Debug.Assert m_pDIB <> 0

    '建立模拟指针
    MakePoint VarPtrArray(pByte), pBytePtr, 1

    Ptr(pBytePtr) = m_pDIB + (m_BI.biHeight - 1) * m_LineBytes 'DIB是逆序存储的
    iLinePtr = pBytePtr.pvData
    For I = 0 To ImgHeight - 1 'Y
        pBytePtr.pvData = iLinePtr
        For J = 0 To ImgWidth - 1 'X
            'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
            pByte(2) = J And &HFF       'Red
            pByte(1) = I And &HFF       'Green
            pByte(0) = (J + K) And &HFF 'Blue
            pBytePtr.pvData = pBytePtr.pvData + 3 '24位
        Next J
        iLinePtr = iLinePtr - m_LineBytes
    Next I

    '释放模拟指针
    FreePoint VarPtrArray(pByte)

    K = (K + 1) And &HFF

End Sub

  1.为了演示DIBSection能够像HBITMAP一样,我在Form_Load中创建了DC、将DIBSection选入DC。同时为了释放,Form_UnLoad中写了释放代码。
  2.在DrawIt中,注意处理部分的代码并不曾与VB_DIB差多少,只不过把索引计算改为地点运算而已。

 

  再来相比较vc的代码,感觉跟真的的指针用法大约嘛:

vc/ImgTestDlg.cpp中CImgTestDlg::DrawIt
void CImgTestDlg::DrawIt() 
{
    int i=0,j=0;
    static int k=0;
    BYTE *pByte, *pLinePtr;

    pLinePtr = (BYTE*)pDIB + (bi.biHeight-1)*LineBytes;
    for(i=0; i<IMGHEIGHT; i++)
    {
        pByte = pLinePtr;
        for(j=0; j<IMGWIDTH; j++)
        {
            pByte[2] = j & 0xff;
            pByte[1] = i & 0xff;
            pByte[0] = (j+k) & 0xff;
            pByte = pByte + 3;
        }
        pLinePtr = pLinePtr - LineBytes;
    }

    k = (k+1) & 0xff;

}

六、结合DirectX

  在GDI中,我们可以用DIB直接操作图像数据,那么号称能直接访问显存的DirectX呢?

 

  一查DirectX
SDK,发现IDirectDrawSurface接口的Lock函数可以锁定表面,从而平素访问该位图数据。再在VB的对象浏览器中细心观望“DirectX
7 for Visual Basic Type Library”,发现DirectDrawSurface7对象也有该格局。

 

  看看5_DX7Ptr:

vb6/5_DX7Ptr/FrmMain.frm中Render
'渲染
Private Sub Render()
    Dim I As Long, J As Long
    Static K As Long
    Dim ddsdInfo As DDSURFACEDESC2
    Dim IsOK As Boolean
    Dim cbPitch As Long
    Dim cbPixel As Long
    Dim iIdxR As Long
    Dim iIdxG As Long
    Dim iIdxB As Long
    Dim iMaxX As Long, iMaxY As Long
    Dim pByte() As Byte, pBytePtr As SAFEARRAY1D
    Dim iLinePtr As Long

    ' check Image
    'Debug.Assert m_pDIB <> 0
    Debug.Assert Not m_ddsRender Is Nothing

    ' Main
    With m_ddsRender
        'Render
        Call .Lock(m_rctSurf, ddsdInfo, DDLOCK_SURFACEMEMORYPTR Or DDLOCK_WRITEONLY Or DDLOCK_NOSYSLOCK Or DDLOCK_WAIT, 0)
        IsOK = CheckPixelFormat(ddsdInfo.ddpfPixelFormat)
        If IsOK Then
            With ddsdInfo
                Debug.Assert .lpSurface
                cbPitch = .lPitch
                With .ddpfPixelFormat
                    cbPixel = (.lRGBBitCount) / 8
                    iIdxR = MaskToRShift(.lRBitMask) / 8
                    iIdxG = MaskToRShift(.lGBitMask) / 8
                    iIdxB = MaskToRShift(.lBBitMask) / 8
                End With
                iMaxX = .lWidth - 1
                iMaxY = .lHeight - 1
            End With

            '建立模拟指针
            MakePoint VarPtrArray(pByte), pBytePtr, 1

            Ptr(pBytePtr) = ddsdInfo.lpSurface
            iLinePtr = pBytePtr.pvData
            For I = 0 To iMaxY 'Y
                pBytePtr.pvData = iLinePtr
                For J = 0 To iMaxX 'X
                    'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                    pByte(iIdxR) = J And &HFF       'Red
                    pByte(iIdxG) = I And &HFF       'Green
                    pByte(iIdxB) = (J + K) And &HFF 'Blue
                    pBytePtr.pvData = pBytePtr.pvData + cbPixel '下一个像素
                Next J
                iLinePtr = iLinePtr + cbPitch '下一个扫描行
            Next I

            '释放模拟指针
            FreePoint VarPtrArray(pByte)

        End If
        Call .Unlock(m_rctSurf)

        'Error Pixel Format
        If IsOK = False Then
            Call .BltColorFill(m_rctSurf, vbBlack)
            Call .SetForeColor(vbWhite)
            Call .DrawText(0, &H20, "Error Pixel Format!", False)
        End If

    End With

    K = (K + 1) And &HFF

End Sub

  1.由于硬件设备质量差异,所以并非指望像DIB那样可以认定RGB字节顺序。应该依据Lock方法传回的DDSURFACEDESC2结构来认同RGB字节顺序(iIdxR、iIdxG、iIdxB)、像素所占字节(cbPixel)及宽距(cbPitch)。
  2.是因为大家有了仿照指针技术,所以那段代码与跟先前4_DIB_Ptr大约,更加是与在VC中使用IDirectDrawSurface::Lock的拍卖代码几乎。

  仔细观察对象浏览器的人会发现,DirectDrawSurface7对象GetLockedArray可以赢得锁定后的位图数据:

vb6/6_DX7Arr/FrmMain.frm中Render
'渲染
Private Sub Render()
    Dim I As Long, J As Long
    Static K As Long
    Dim ddsdInfo As DDSURFACEDESC2
    Dim IsOK As Boolean
    Dim cbPitch As Long
    Dim cbPixel As Long
    Dim iIdxR As Long
    Dim iIdxG As Long
    Dim iIdxB As Long
    Dim iMaxX As Long, iMaxY As Long
    Dim pByte() As Byte
    Dim iCurPtr As Long

    ' check Image
    'Debug.Assert m_pDIB <> 0
    Debug.Assert Not m_ddsRender Is Nothing

    ' Main
    With m_ddsRender
        'Render
        Call .Lock(m_rctSurf, ddsdInfo, DDLOCK_SURFACEMEMORYPTR Or DDLOCK_WRITEONLY Or DDLOCK_NOSYSLOCK Or DDLOCK_WAIT, 0)
        IsOK = CheckPixelFormat(ddsdInfo.ddpfPixelFormat)
        If IsOK Then
            With ddsdInfo
                Debug.Assert .lpSurface
                cbPitch = .lPitch
                With .ddpfPixelFormat
                    cbPixel = (.lRGBBitCount) / 8
                    iIdxR = MaskToRShift(.lRBitMask) / 8
                    iIdxG = MaskToRShift(.lGBitMask) / 8
                    iIdxB = MaskToRShift(.lBBitMask) / 8
                End With
                iMaxX = .lWidth - 1
                iMaxY = .lHeight - 1
            End With

            Call .GetLockedArray(pByte)

            For I = 0 To iMaxY 'Y
                iCurPtr = 0
                For J = 0 To iMaxX 'X
                    'PicView.PSet (J, I), RGB(J And &HFF, I And &HFF, (J + K) And &HFF)
                    pByte(iCurPtr + iIdxR, I) = J And &HFF      'Red
                    pByte(iCurPtr + iIdxG, I) = I And &HFF      'Green
                    pByte(iCurPtr + iIdxB, I) = (J + K) And &HFF 'Blue
                    iCurPtr = iCurPtr + cbPixel '下一个像素
                Next J
            Next I

        End If
        Call .Unlock(m_rctSurf)

        'Error Pixel Format
        If IsOK = False Then
            Call .BltColorFill(m_rctSurf, vbBlack)
            Call .SetForeColor(vbWhite)
            Call .DrawText(0, &H20, "Error Pixel Format!", False)
        End If

    End With

    K = (K + 1) And &HFF

End Sub

  这么些就是合法推荐做法,使用一个二维数组来拍卖位图数据。不过就是因为其行使的是二维数组,所以它比一维数组的一成不变指针慢一些。借使你不想使用尤其技术(模拟指针),可以行使GetLockedArray。
  注意到那一点没有,GetLockedArray是一个办法而不是一个质量,大家须要将数组变量传递过去,然后就足以行使该数组直接操作位图数据了。啊哈!驾驭了GetLockedArray也是选择模拟指针技术完毕的,只可是它填充的是2维的SAFEARRAY结构而已。

  注意:只操作位于系统内存的外表,千万别操作对显存中的表面。那是因为CPU访问显存比返问内存要慢许多!那几个提出并不是纯属的,假若对该表面的Blt的调用次数远领先友爱写的图像处理操作的话,可以将该表面放在显存,或者是在内存中总计好后再三回性交给到显存。
  本程序建立图像处理操作的外表的代码:

vb6/5_DX7Ptr/FrmMain.frm中CreateSurfaces
    ' init Image
    With ddsdInfo
        .lFlags = DDSD_CAPS Or DDSD_HEIGHT Or DDSD_WIDTH Or DDSD_PIXELFORMAT
        With .ddsCaps
            .lCaps = DDSCAPS_OFFSCREENPLAIN Or DDSCAPS_SYSTEMMEMORY 'CPU访问内存比访问显存快
        End With
        .lWidth = ImgWidth
        .lHeight = ImgHeight
        .ddpfPixelFormat = m_ddsdInfo.ddpfPixelFormat
    End With
    Set m_ddsRender = m_dxDraw.CreateSurface(ddsdInfo)

(全文完)

不知情怎么上传文件,下载请到那里:
http://blog.gameres.com/thread.asp?BlogID=2143&threadid=55903

发表评论

电子邮件地址不会被公开。 必填项已用*标注