int SetDIBitsToDevice(
HDC hdc, // handle to DC
int XDest, // x-coord of destination upper-left corner
int YDest, // y-coord of destination upper-left corner
DWORD dwWidth, // source rectangle width
DWORD dwHeight, // source rectangle height
int XSrc, // x-coord of source lower-left corner
int YSrc, // y-coord of source lower-left corner
UINT uStartScan, // first scan line in array
UINT cScanLines, // number of scan lines
CONST VOID *lpvBits, // array of DIB bits
CONST BITMAPINFO *lpbmi, // bitmap information
UINT fuColorUse // RGB or palette indexes
);
个人对SetDIBitsToDevice函数的理解,说句实话这个函数的msdn说的让人很晦涩难懂,我把自己的理
解和大家分享一下,不知道正确与否。首先不要去管什么源左下角坐标和目标左上角的说法。这个左下
和右上的概念是相对于笛卡尔坐标系(屏幕左下角为坐标原点)而言。而我们函数使用时参数msdn指定
的是逻辑坐标系,如果采用默认的映射方式(MM_TEXT)的映射方式,映射到设备DC上的坐标系的坐标
原点是屏幕左上角,正方向为向右和向下。换句话说对于从下而上的位图而言,msdn的说法是有问题的
,因为存储在内存中的从下而上的dib位图文件相对于逻辑坐标系而言,(xSrc, ySrc)实际上是dib位图
文件的左上角,而(xDst, yDst)是左下角。
下面就我的理解解释一下这些参数到底做了些什么,如果照搬msdn当然也就不用写了,我是用我的通俗
的理解方法,希望给大家有点帮助。
1.XSrc, YSrc,dwWidth, dwHeight这四个参数决定了一个源矩形,现在假设在内存中有这样一块源矩形
大小的空白画板。现在只是有这样一块空白画板,至于画什么,由第2步的参数决定。
2. 现在有这样一块空白画板了。lpvBits,cScanLines这两个参数最为重要,他们联合决定在空画板上
画什么内容。lpvBits指向的是你要显示的字节类型的位图数组,cScanLines表示的是该位图数组中的
扫描线数目。通俗的说就是:lpvBits指定了你要从整个位图的哪一行开始扫描,cScanLines指定了你
要扫描多少行。这两个参数一决定,实际上就在你从内存中加载的位图中相当于截取了图形的某一块。
3.uStartScan这个参数不得不提,这个参数很容易出错,不要将这个参数理解成用来选定位图数据的起
始行,其实这个参数指定的是在第1步所决定的空白画板中的什么位置开始显示由第2步的两个参数截取
的图形(这个图形可能是整个位图,也可能只是图形的一部分)。
4.最后一步根据映射关系,将由源矩形确定的画板上画好的内容(第1,2,3步已经指明在画板什么位置
,画上从内存中加载的位图的哪一部分)映射到目标矩形,也就是我们真实的在目标设备dc中看到的图
像。如果是默认的映射方式,查看源坐标和目标坐标的映射表会发现,就是将源矩形进行了上下翻转,
就得到真实的在屏幕上看到目标图像。
参考《windows程序设计》,修改程序15-3 的APOLLO11程序中的参数,我们来看看效果。具体的程序较
长,我没有贴出来。我们就看SetDIBitsToDevice函数的修改参数后的效果。
// Bottom-up DIB full size
SetDIBitsToDevice (hdc,
0, // xDst
0, // yDst
cxDib[0], // cxSrc
cyDib[0], // cySrc
0 , // xSrc
0, // ySrc
0, // 从什么位置开始显示dib
cyDib[0]/2, // 扫描多少行
pBits[0], //指定从dib的什么位置开始扫描
pbmi[0],
DIB_RGB_COLORS) ;
注意本程序中 pBits[0] = (BYTE *) pbmfh[0] + pbmfh[0]->bfOffBits ;指向的是内存中dib的第一行
那么函数实现的效果就是。将dib位图的上半部分截取下来,放到起始位置为0的大小为源矩形大小的
画板中。然后上下翻转图形,即可看到真实显示在屏幕上的图形效果。
从程序运行结果来看,得到了宇航员的身体的下半部分。如果要得到上半部分即看到宇航员的头怎么做
呢,千万别告诉我修改uStartScan参数啊,不然我说半天白说了。正确的方法是修改lpvBits指针,让
它指向dib的下半部分,让它扫描dib的下半部分,这样就可以看到头了。具体操作就是先要计算dib位
图的每一行的宽度。然后乘以高度的1/2,就可以使lpvBits指针指向dii位图的中间。然后往下扫描就可以了。
int dibWidth = cxDib[0]; //计算dib每一行的字节数,注意默认是4字节对齐的,加个判断
if(dibWidth % 4 == 0)
{
dibWidth = cxDib[0];
}else
dibWidth = cxDib[0] + 4 - cxDib[0] / 4;
pBits[0] = (BYTE *) pbmfh[0] + pbmfh[0]->bfOffBits + dibWidth * cyDib [0] / 2;
再次调用
SetDIBitsToDevice (hdc,
0, // xDst
0, // yDst
cxDib[0], // cxSrc
cyDib[0], // cySrc
0 , // xSrc
0, // ySrc
0, // 从什么位置开始显示dib
cyDib[0]/2, // 扫描多少行
pBits[0], //指定从dib的什么位置开始扫描
pbmi[0],
DIB_RGB_COLORS) ;