文章目錄
- 1. 顯示窗口(harib08d)
- 2. 消除閃爍1(harib08g)
- 3. 消除閃爍2(harib08h)
本章的前三節做了如下修改:
- 解決了鼠標無法隱藏在屏幕右側和下側的問題。
- 當鼠標隱藏在右側時會在屏幕最左側產生陰影的問題。
- 在struct SHEET結構體中添加struct SHTCTL類型指針,以減少接口中參數列表的個數。
1. 顯示窗口(harib08d)
類似制作背景和鼠標,需要先準備一個圖層,然后在圖層buffer中描繪一個類似窗口的圖。make_window8
函數類似graphic.c
中的init_screen8
函數,其中“x”按鈕使用類似init_mouse_cursor8
的方法。
void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
{static char closebtn[14][16] = {"OOOOOOOOOOOOOOO@","OQQQQQQQQQQQQQ$@","OQQQQQQQQQQQQQ$@","OQQQ@@QQQQ@@QQ$@","OQQQQ@@QQ@@QQQ$@","OQQQQQ@@@@QQQQ$@","OQQQQQQ@@QQQQQ$@","OQQQQQ@@@@QQQQ$@","OQQQQ@@QQ@@QQQ$@","OQQQ@@QQQQ@@QQ$@","OQQQQQQQQQQQQQ$@","OQQQQQQQQQQQQQ$@","O$$$$$$$$$$$$$$@","@@@@@@@@@@@@@@@@"};int x, y;char c;boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, xsize - 1, 0 );boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, xsize - 2, 1 );boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 1);boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, 1, ysize - 2);boxfill8(buf, xsize, COL8_848484, xsize - 2, 1, xsize - 2, ysize - 2);boxfill8(buf, xsize, COL8_000000, xsize - 1, 0, xsize - 1, ysize - 1);boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);boxfill8(buf, xsize, COL8_000084, 3, 3, xsize - 4, 20 );boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);for (y = 0; y < 14; y++) {for (x = 0; x < 16; x++) {c = closebtn[y][x];if (c == '@') {c = COL8_000000;} else if (c == '$') {c = COL8_848484;} else if (c == 'Q') {c = COL8_C6C6C6;} else {c = COL8_FFFFFF;}buf[(5 + y) * xsize + (xsize - 21 + x)] = c;}}return;
}
在HariMain函數中相應的調用窗口函數和圖層處理函數,顯示效果:
2. 消除閃爍1(harib08g)
圖層刷新的規則:
- 如果窗口圖層變化,無鼠標圖層,則刷新窗口圖層,不需要刷新背景圖層。
- 如果窗口圖層變化,鼠標在其上,則刷新窗口圖層的同時刷新鼠標圖層(因為窗口的刷新會覆蓋鼠標的一部分顯示區域),同樣不需要刷新背景圖層。
首先修改sheet_refreshsub
函數,在參數列表中追加h0
入參,表示只刷新在此h0
圖層及以上的圖層。對應的sheet處理函數有對sheet_refreshsub
的調用都需要修改。
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{// 略for (h = h0; h <= ctl->top; h++) {// 略}return;
}
其中,在sheet_slide
函數中,圖層移動時會導致下面的圖層露出,因此需要從下面的圖層開始刷新。在圖層所移動到的目標位置處,比新移來的圖層低的圖層沒有什么變化,只是需要隱藏一部分,所以只需要刷新移動圖層和它上面的圖層。
在sheet_updown
函數中,同樣的思路,針對個別不需要自下而上全部刷新的圖層,僅刷新其局部。
查看效果,窗口圖層已經不再閃爍了,只是如果鼠標放置在上面,還是會產生閃爍。
3. 消除閃爍2(harib08h)
鼠標的閃爍顯現是由于一會描繪一會消除產生的。如果期望消除閃爍,就需要在刷新窗口時,避開鼠標覆蓋的區域對VRAM的寫入處理。
開辟一塊內存,大小與VRAM相同,稱其為map。這塊內存用來表示畫面上的點是哪個圖層的像素,因此可以理解為圖層的地圖。
如圖,期望刷新圖層1的時候,參照map的內容刷新,就不必擔心圖層1和圖層2的重疊部分了。
struct SHTCTL {unsigned char *vram, *map;int xsize, ysize, top;struct SHEET *sheets[MAX_SHEETS];struct SHEET sheets0[MAX_SHEETS];
};/* 向圖層中寫入號碼 */
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{int h, bx, by, vx, vy, bx0, by0, bx1, by1;unsigned char *buf, sid, *map = ctl->map;struct SHEET *sht;if (vx0 < 0) { vx0 = 0; }if (vy0 < 0) { vy0 = 0; }if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }for (h = h0; h <= ctl->top; h++) {sht = ctl->sheets[h];sid = sht - ctl->sheets0; /* 將進行了減法運算的地址作為圖層的號碼 */buf = sht->buf;bx0 = vx0 - sht->vx0;by0 = vy0 - sht->vy0;bx1 = vx1 - sht->vx0;by1 = vy1 - sht->vy0;if (bx0 < 0) { bx0 = 0; }if (by0 < 0) { by0 = 0; }if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }if (by1 > sht->bysize) { by1 = sht->bysize; }for (by = by0; by < by1; by++) {vy = sht->vy0 + by;for (bx = bx0; bx < bx1; bx++) {vx = sht->vx0 + bx;if (buf[by * sht->bxsize + bx] != sht->col_inv) {map[vy * ctl->xsize + vx] = sid;}}}}return;
}void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
{int h, bx, by, vx, vy, bx0, by0, bx1, by1;unsigned char *buf, *vram = ctl->vram, *map = ctl->map, sid;struct SHEET *sht;/* 如果refresh的范圍超出了畫面則修正 */if (vx0 < 0) { vx0 = 0; }if (vy0 < 0) { vy0 = 0; }if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }for (h = h0; h <= h1; h++) {sht = ctl->sheets[h];buf = sht->buf;sid = sht - ctl->sheets0;/* 利用vx0 ~ vy1,對bx0 ~ by1倒推 */bx0 = vx0 - sht->vx0;by0 = vy0 - sht->vy0;bx1 = vx1 - sht->vx0;by1 = vy1 - sht->vy0;if (bx0 < 0) { bx0 = 0; }if (by0 < 0) { by0 = 0; }if (bx1 > sht->bxsize) { bx1 = sht->bxsize; }if (by1 > sht->bysize) { by1 = sht->bysize; }for (by = by0; by < by1; by++) {vy = sht->vy0 + by;for (bx = bx0; bx < bx1; bx++) {vx = sht->vx0 + bx;if (map[vy * ctl->xsize + vx] == sid) { // 修改點vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];}}}}return;
}void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1)
{if (sht->height >= 0) { /* 如果正在顯示,則按照新圖層的信息進行刷新 */sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height, sht->height);}return;
}void sheet_slide(struct SHEET *sht, int vx0, int vy0)
{struct SHTCTL *ctl = sht->ctl;int old_vx0 = sht->vx0, old_vy0 = sht->vy0;sht->vx0 = vx0;sht->vy0 = vy0;if (sht->height >= 0) { /* 如果正在顯示,則按照新圖層的信息進行刷新 */sheet_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);sheet_refreshmap(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0, sht->height - 1);sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height);}return;
}
在sheet_refresh
函數里,由于圖層的上下關系沒有變,所以不需要重新進行refreshmap
的處理。
在sheet_slide
函數里,需要先重寫map,分別對應移動前后的圖層,然后調用sheet_refreshsub
函數。在移動前的地方,只針對上層圖層移走后而露出的下層圖層進行重繪就行了,在移動目的地處僅重繪了一張移動過去的圖層。
sheet_updown
函數,同樣在調用sheet_refreshsub
之前,需要先執行sheet_refreshmap
重新繪制map。
此時的屏幕將不再閃爍,看起來像是一個比較現代化的電腦了。