[Ref.http://jixiang1119.blog.163.com/blog/static/2827097320108342559871/]
SetDIBitsToDevice函數的理解
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) ;
看效果: