• 《30天自制操作系统》笔记(08)——叠加窗口刷新


    《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天自制操作系统》笔记(09)——绘制窗口》

  • 相关阅读:
    定理环境
    tcolorbox 宏包简明教程
    【专访】南科大数学系何炳生教授——四十年上下求索
    研究生导师为什么喜欢问学生家境?
    高德纳谈《具体数学》的诞生
    剑桥大学
    线性代数
    APPCAN的mas服务报错
    github删除仓库
    Angular2入门教程-2 实现TodoList App
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/OS-in-30-days-08-refresh-windows.html
Copyright © 2020-2023  润新知