• 沒有找到結果。

3.3 vncClientThread

3.4.4 SendUpdate

這個函數是 VNC Server 輸出的部分,它會從桌面的 Framebuffer 上取出,

由 TriggerUpdate 函數中獲得的可能變動區域的最新 Pixel 資料,將這些資料更 新到 mainbuff,並和 backbuff 做比對,再將真正有變動的部分記錄下來,最後 壓縮這些真正變動區塊資料並傳送給 Client。下圖是此函數的流程圖。接著我們 會循序漸進的說明這些細節部分。

圖 3-31.SendUpdate 流程圖

●Region to Rectangles

在 TriggerUpdate 中,rgncache 記錄著最後可能的變動區域,而 rgncache 是 Windows 的一種類別:Region,它能夠記錄不規則的形狀區域,因此,我們 可以在 vncDesktopThread 先記錄 Hook 判斷的可能範圍,再記錄 TriggerUpdate 中額外的區域!那這不規則的區域又要如何使用呢?

DWORD GetRegionData(

HRGN hRgn, // Region 的名稱

DWORD dwCount, // size of region data buffer LPRGNDATA lpRgnData // region data buffer

);

GetRegionData 是 Windows 的 SDK 函數,它可以把不規則的 Region 切成一連 串的矩形結構,放入 lpRgnData 中,因此它承載著組成此 Region 的所有矩形,

因此只要將他們一個一個取出來處理即可。假設有由 TrigguerUpdate 獲得的區 域為 Region1,如下圖,當使用了 GetRegionData 函數,它便會將此 Region 以 橫軸為主,分割成許多個小矩形,將這些矩形的結構以類似陣列的方式,置放在 lpRgnData 中,如圖 30。此即為組成此 region 的矩形來源,稍後會陸續用到。

圖 3-32.Region1 示意圖

圖 3-33.使用 GetRegionData 函數後

●Capture Screen

在 vncDesktopThread 初始化中的 EnableOptimisedBlits()函數,就有提到,

為了讓 mainbuff 可以迅速獲取桌面 Framebuffer 最新資料,那時候有設定 DIB Section,則此時只需要兩個步驟,mainbuff 就可得到最新的 Pixel 資料。

圖 3-34.Capture Screen

1. 將 DIB Section 放入一開始建好的 m_hmemdc 中,讓他們同步。

2. 從螢幕桌面的 Device Context 直接用 BitBlt 函數,從 m_hrootdc 複製所有可 能變動的矩形位置資料到 m_hmemdc 中,此時 DIB Section 也會跟著更新,

而 mainbuff 的指標位置就是指向對應 DIB Section 的 bitmap 陣列,由此串 連起來,每跑一次,mainbuff 的資料就會更新成目前桌面最新的畫面。

BitBlt 函數是用來傳送一個區塊的位元資料 ( bit-block transfer ),意即使在記 憶體內的位元圖,傳送到目的端的 Device Context。

BOOL BitBlt(

HDC hdcDest, // 目的地 DC 的 handle int nXDest, // 目的地 DC 的左上 x 座標 int nYDest, // 目的地 DC 的左上 y 座標 int nWidth, // 傳送資料區塊的寬 int nHeight, // 傳送資料區塊的高 HDC hdcSrc, // 來源 DC 的 handle

int nXSrc, // 來源地 DC 的左上 x 座標 int nYSrc, // 來源地 DC 的左上 y 座標 DWORD dwRop // 操作的參數

);

●Get Change Region

將 mainbuff 的資料更新成最新的狀態後,接著要進行最後的比對,因為整 個可能變動區域中,有些是為了避免誤判額外加入的,但實際卻沒有變動到,如 果沒有變動範圍的資料也處理的話,會造成傳輸到 Client 的量太大且無意義,因 此,這個地方最大的目的是找出真正有變動的部分,並只對這些部分做壓縮和傳 輸。

在 vncDesktopThread 迴圈中,我們獲得了最後變動區域,由 GetRegionData 可以得到這個區域矩形陣列。在 Capture Screen 函數中,將桌面對應矩形最新畫 面更新到 mainbuff 中,但 backbuff 仍然是舊的畫面資料,因此在這兩個 buffer 中比對這些矩形區域,只要有不相同的地方,就代表是真正發生變動的區域。因 為 mainbuff 和 backbuff 都是記憶體,且兩個 buff 都有指標可以指向他們的記憶 體位置,因此,只要知道畫面每個像素幾位元和兩個 buffer 各自的指標位置,便 可算出對應每個矩形的記憶體起始位置。一開始會先將矩形切成許多 32*32 pixel 的區塊,旁邊不足 32 的用餘數當大小。從左至右,從上至下,以 32*1 pixel 為 單位(邊邊不足 32 的區塊以餘數*1 為單位),比較 mainbuff 和 backbuff 對應的 區塊,當有不同的 32*1 出現時,將不同的地方記錄在 toBeSent 的 Region 中,

並從 mainbuff 中複製這個 32*1 的資料到 backbuff,這樣的動作會一個矩形接著 一個矩形,完成後,toBeSent 中便會記錄著最後真正有變動的部分,且 mainbuff 和 backbuff 內的資料會更新成一模一樣,直到下一次的比對。

圖 3-35.toBeSent

●Compress and Send Data

在我們取得真正變動區域,並記錄在 toBeSent 中,接著我們也是使用 GetRegionData()將組成 toBeSent 的所有矩形取出,並對他們做處理,如下圖。

圖 3-36.FramebufferUpdate 訊息

取得變動矩形後,我們要將這些矩形的資料,以 FramebufferUpdate 的訊息傳送 給 Client。訊息第一個參數是 Message Type,說明這是何訊息,接著說明這次更 新共有幾個小矩形資料所組成,最後會接著這些矩形的資料。每個矩形資料又分 為標頭和真正畫面壓縮資料,標頭記載著這個小矩形的位置座標和大小,讓 Client 可將這個矩形資料更新至正確的位置。