《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下我测试的时候根本没有差别,未优化的版本也不卡)
有了本篇的准备,下一步就可以制作和显示窗口了。
《30天自制操作系统》笔记(08)——叠加窗口刷新,布布扣,bubuko.com