《30天自制操作系統》筆記(08)——疊加窗口刷新
進度回顧
上一篇中介紹了內存管理的思路和算法,我們已經可以動態申請和釋放內存了。這不就是堆(Heap)么。在此基礎上,本篇要做一段程序,一并解決窗口和鼠標的疊加處理問題。
問題
在之前的《《30天自制操作系統》筆記(05)——啟用鼠標鍵盤》篇,已經能夠移動鼠標了。但是遺留了如下圖所示的一個小問題。
我們希望的情形是這樣的:
實際上,當前版本的OS還沒有窗口圖層的東西。本篇要做一段程序,一并解決窗口和鼠標的疊加處理問題。
在屏幕上顯示多個窗口,類似于photoshop中顯示多個圖層。從桌面壁紙到每個窗口(層)到最上層的鼠標(鼠標也視為一個小窗口),將繪制了圖案的透明圖層疊加起來。
最初版解決方案
首先定義圖層的數據結構。
1 #define MAX_SHEETS 256 2 struct SHEET { 3 unsigned char *buf; 4 int bxsize, bysize, vx0, vy0, col_inv, height, flags; 5 }; 6 struct SHTCTL { 7 unsigned char *vram; 8 int xsize, ysize, top; 9 struct SHEET *sheets[MAX_SHEETS]; 10 struct SHEET sheets0[MAX_SHEETS]; 11 };
原作者用sheet表示圖層,看來英文很一般,用 layer似乎更恰當。
圖層層次變更(當前窗口變更)、圖層位置移動(窗口位置移動)這些代碼實在沒什么可說。刷新函數也很簡單,就是從下(桌面壁紙)往上(鼠標),將透明以外的所有像素復制到VRAM中。代碼如下。
1 void sheet_refresh(struct SHTCTL *ctl) 2 { 3 int h, bx, by, vx, vy; 4 unsigned char *buf, c, *vram = ctl->vram; 5 struct SHEET *sht; 6 for (h = 0; h <= ctl->top; h++) { 7 sht = ctl->sheets[h]; 8 buf = sht->buf; 9 for (by = 0; by < sht->bysize; by++) { 10 vy = sht->vy0 + by; 11 for (bx = 0; bx < sht->bxsize; bx++) { 12 vx = sht->vx0 + bx; 13 c = buf[by * sht->bxsize + bx]; 14 if (c != sht->col_inv) { 15 vram[vy * ctl->xsize + vx] = c; 16 } 17 } 18 } 19 } 20 return; 21 }
很明顯這樣太沒效率了。下面就對刷新功能進行優化。
優化1-移動優化
鼠標層只有16*16=256個像素。但是根據上文的代碼,只要鼠標稍微動一下,OS就要重繪320*200=64000個像素。這是不必要的。只需重繪移動前后的部分即256*2=512個像素就可了。512只是64000的0.8%。以后啟用高分辨率了,性能提升會更多。
1 void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1) 2 { 3 int h, bx, by, vx, vy; 4 unsigned char *buf, c, *vram = ctl->vram; 5 struct SHEET *sht; 6 for (h = 0; h <= ctl->top; h++) { 7 sht = ctl->sheets[h]; 8 buf = sht->buf; 9 for (by = 0; by < sht->bysize; by++) { 10 vy = sht->vy0 + by; 11 for (bx = 0; bx < sht->bxsize; bx++) { 12 vx = sht->vx0 + bx; 13 if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) { 14 c = buf[by * sht->bxsize + bx]; 15 if (c != sht->col_inv) { 16 vram[vy * ctl->xsize + vx] = c; 17 } 18 } 19 } 20 } 21 } 22 return; 23 }
窗口(鼠標)移動時,只需先調用此函數重繪移動前的部分,再調用此函數重繪移動后的部分就行了。
優化2-文字優化
移動鼠標時,由于要在桌面上顯示坐標等信息,又被迫重繪了整個桌面,所以還是很慢。下面來優化這個瓶頸。
原理和優化1是一樣的。只重繪文字所在的部分就行了。不再贅述。
優化3-減少判定
在上文的"sheet_refreshsub"函數中,使用了長長的"if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1)"判定。但對于窗口外(即透明色)的位置,根本不用重繪,所以這個判定也就不需要了。我們就把這一點優化一下,只更新窗口所在的矩形范圍內的地方。
1 void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1) 2 { 3 int h, bx, by, vx, vy, bx0, by0, bx1, by1; 4 unsigned char *buf, c, *vram = ctl->vram; 5 struct SHEET *sht; 6 for (h = 0; h <= ctl->top; h++) { 7 sht = ctl->sheets[h]; 8 buf = sht->buf; 9 /* 使用vx0~vy1,對bx0~by1進行倒推*/ 10 bx0 = vx0 - sht->vx0; 11 by0 = vy0 - sht->vy0; 12 bx1 = vx1 - sht->vx0; 13 by1 = vy1 - sht->vy0; 14 if (bx0 < 0) { bx0 = 0; } 15 if (by0 < 0) { by0 = 0; } 16 if (bx1 > sht->bxsize) { bx1 = sht->bxsize; } 17 if (by1 > sht->bysize) { by1 = sht->bysize; } 18 for (by = by0; by < by1; by++) { 19 vy = sht->vy0 + by; 20 for (bx = bx0; bx < bx1; bx++) { 21 vx = sht->vx0 + bx; 22 c = buf[by * sht->bxsize + bx]; 23 if (c != sht->col_inv) { 24 vram[vy * ctl->xsize + vx] = c; 25 } 26 } 27 } 28 } 29 return; 30 }
說實話原作者的變量命名還是有點晦澀。理解原理就可以了,代碼不需費勁看,因為真的很簡單。
總結
本篇雖然沒有在桌面上畫出類似windows應用程序窗口那樣的窗口,但是已經為其準備好了重繪的數據結構和算法。而且對算法進行優化,雖然優化原理及其簡單(縮小不必要的重繪范圍),但是效果很好。話是這么說,這個優化效果就沒辦法用圖片展示了,自己在本地分別運行一下優化前后的版本吧還是。(建議用VMware,這個能看到很明顯的差別,在QEMU下我測試的時候根本沒有差別,未優化的版本也不卡)
有了本篇的準備,下一步就可以制作和顯示窗口了。
文章列表