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

从而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_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文件格式(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机使用小端规则(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(为兼容BMP4/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(为兼容BMP4/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(为兼容BMP4/5如声明成Any),对DIB的格式进行描述的一个构造

fdwInit

如果无应允针对各项图数据开展初始化,那么只要为零星。如一旦为CBM_INIT,表示根据lpbInit和
lpbmi参数对各项图进行初始化

lpbInit

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

lpbmi

指向BITMAPINFO(为兼容BMP4/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(为兼容BMP4/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(为兼容BMP4/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

发表评论

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