《30天自制操作系统》笔记(08)——叠加窗口刷新
进度回顾
中介绍了内存管理的思路和算法,我们已经可以动态申请和释放内存了。这不就是堆(Heap)么。在此基础上,本篇要做一段程序,一并解决窗口和鼠标的叠加处理问题。
问题
在之前的篇,已经能够移动鼠标了。但是遗留了如下图所示的一个小问题。
我们希望的情形是这样的:
实际上,当前版本的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下我测试的时候根本没有差别,未优化的版本也不卡)
有了本篇的准备,下一步就可以制作和显示窗口了。