• 《30天自制操作系统》笔记(09)——绘制窗口


    《30天自制操作系统》笔记(09)——绘制窗口

    进度回顾

    上一篇中介绍了图层式窗口管理的思路和算法。在此基础上,本篇就解决绘制窗口及其简单的优化问题。

    这里稍微吐槽一下《30天自制操作系统》原作者。全书我刚刚看了三分之一,写得确实不错,但是我能感受到原作者是习惯用汇编语言和汇编思维来写程序的。虽然书里尽量使用了C语言,但给我一种用C写汇编的感觉。也可能是原作者故意简化了OS开发过程,方便初学者理解吧。幸好这在我预料之中,一开始我就打算先看完这本书然后自己再从零设计OS的。

    看似立体的窗口其实是画出来的

    从第一次见到电脑屏幕上的窗口开始,我就想知道它是怎么做到的。一直在怀疑,现在终于确信了看似立体的窗口、按钮什么的,全是画出来的。

     
     1 void make_window8(unsigned char *buf, int xsize, int ysize, char *title)
     2 {
     3     static char closebtn[14][16] = {
     4         "OOOOOOOOOOOOOOO@",
     5         "OQQQQQQQQQQQQQ$@",
     6         "OQQQQQQQQQQQQQ$@",
     7         "OQQQ@@QQQQ@@QQ$@",
     8         "OQQQQ@@QQ@@QQQ$@",
     9         "OQQQQQ@@@@QQQQ$@",
    10         "OQQQQQQ@@QQQQQ$@",
    11         "OQQQQQ@@@@QQQQ$@",
    12         "OQQQQ@@QQ@@QQQ$@",
    13         "OQQQ@@QQQQ@@QQ$@",
    14         "OQQQQQQQQQQQQQ$@",
    15         "OQQQQQQQQQQQQQ$@",
    16         "O$$$$$$$$$$$$$$@",
    17         "@@@@@@@@@@@@@@@@"
    18     };
    19     int x, y;
    20     char c;
    21     boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         xsize - 1, 0        );
    22     boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         xsize - 2, 1        );
    23     boxfill8(buf, xsize, COL8_C6C6C6, 0,         0,         0,         ysize - 1);
    24     boxfill8(buf, xsize, COL8_FFFFFF, 1,         1,         1,         ysize - 2);
    25     boxfill8(buf, xsize, COL8_848484, xsize - 2, 1,         xsize - 2, ysize - 2);
    26     boxfill8(buf, xsize, COL8_000000, xsize - 1, 0,         xsize - 1, ysize - 1);
    27     boxfill8(buf, xsize, COL8_C6C6C6, 2,         2,         xsize - 3, ysize - 3);
    28     boxfill8(buf, xsize, COL8_000084, 3,         3,         xsize - 4, 20       );
    29     boxfill8(buf, xsize, COL8_848484, 1,         ysize - 2, xsize - 2, ysize - 2);
    30     boxfill8(buf, xsize, COL8_000000, 0,         ysize - 1, xsize - 1, ysize - 1);
    31     putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);
    32     for (y = 0; y < 14; y++) {
    33         for (x = 0; x < 16; x++) {
    34             c = closebtn[y][x];
    35             if (c == '@') {
    36                 c = COL8_000000;
    37             } else if (c == '$') {
    38                 c = COL8_848484;
    39             } else if (c == 'Q') {
    40                 c = COL8_C6C6C6;
    41             } else {
    42                 c = COL8_FFFFFF;
    43             }
    44             buf[(5 + y) * xsize + (xsize - 21 + x)] = c;
    45         }
    46     }
    47     return;
    48 }

    这个绘制"关闭"按钮的代码和绘制鼠标的代码多么相似!其实都是画出来的立体效果而已。

    当然,这么静态地在主函数里绘制一次还不够,还要给窗口添加一个图层,并注册到OS的sheetctrl链表中。这样,当窗口发生激活、移动时,sheetctrl就会刷新这个窗口了。

    优化1-减少需要刷新的图层

    把"每次刷新都遍历所有图层"改进为"仅对内容发生变化的图层及其以上的图层进行刷新"。

    1 void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
    2 {
    3     /**/
    4     for (h = h0; h <= ctl->top; h++) {
    5         /**/
    6     }
    7     return;
    8 }

    调用sheet_refreshsub()函数的位置都要相应修改。

     1 void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1)
     2 {
     3     if (sht->height >= 0) { /* 如果正在显示,则按新图层的信息进行刷新 */
     4         sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height);
     5     }
     6     return;
     7 }
     8 
     9 void sheet_slide(struct SHEET *sht, int vx0, int vy0)
    10 {
    11     int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
    12     sht->vx0 = vx0;
    13     sht->vy0 = vy0;
    14     if (sht->height >= 0) { /* 如果正在显示,则按新图层的信息进行刷新 */
    15         sheet_refreshsub(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
    16         sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);
    17     }
    18     return;
    19 }
    20 
    21 void sheet_updown(struct SHEET *sht, int height)
    22 {
    23     /**/
    24 
    25     /* 以下主要是对sheets[]的重新排列 */
    26     if (old > height) {    /* 比以前低 */
    27         if (height >= 0) {
    28             /* 中间的提起 */
    29             for (h = old; h > height; h--) {
    30                 ctl->sheets[h] = ctl->sheets[h - 1];
    31                 ctl->sheets[h]->height = h;
    32             }
    33             ctl->sheets[height] = sht;
    34             sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height + 1);
    35         } else {    /* 隐藏 */
    36             if (ctl->top > old) {
    37                 /* 把上面的降下来 */
    38                 for (h = old; h < ctl->top; h++) {
    39                     ctl->sheets[h] = ctl->sheets[h + 1];
    40                     ctl->sheets[h]->height = h;
    41                 }
    42             }
    43             ctl->top--; /* 正在显示的图层减少了一个,故最上面的高度也减少 */
    44             sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, 0);
    45         }
    46     } else if (old < height) {    /* 比以前高 */
    47         if (old >= 0) {
    48             /* 中间的图层往下降一层 */
    49             for (h = old; h < height; h++) {
    50                 ctl->sheets[h] = ctl->sheets[h + 1];
    51                 ctl->sheets[h]->height = h;
    52             }
    53             ctl->sheets[height] = sht;
    54         } else {    /*从隐藏状态变为显示状态 */
    55             /* 把在上面的图层往上提高一层 */
    56             for (h = ctl->top; h >= height; h--) {
    57                 ctl->sheets[h + 1] = ctl->sheets[h];
    58                 ctl->sheets[h + 1]->height = h + 1;
    59             }
    60             ctl->sheets[height] = sht;
    61             ctl->top++; /* 显示中的图层增加了一个,故最上面的高度也增加 */
    62         }
    63         sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize, height);
    64     }
    65     return;
    66 }
    修改相应的位置

    在sheet_slide函数里,图层的移动有时会导致下面的图层露出,所以要从最下面开始刷新。另一方面,在移动目标处,比新移来的图层位置还要低的图层没有什么变化,而且只是隐藏起来了,所以只要刷新移动的图层和它上面的图层就可以了。

    优化2-借助图层map

    这是个空间换时间的优化方式。

    首先,开辟一块内存,大小和VRAM一样,我们称之为map。这个map用来表示画面上的点是哪个图层的像素,所以它相当于图层的地图。

    文字不好表达清楚,看图则一目了然。

    当刷新图层1的时候,如果对照这个map刷新,就不会覆盖被其他图层挡住的部分了。

    原理就是这样,并不高深。代码也很简单,而且不必要看了。

    效果如下。

    总结

    本篇终于能够画出类似windows应用程序窗口那样的窗口。原来那么立体的窗口真是一点一点画出来的。解决了10多年的疑问,真舒服。

    原作者对绘制窗口这个问题进行了多次优化,效果很好。不知道windows的窗口绘制是如何优化的,不知道《30天自制操作系统》后续章节还有没有继续优化?革命尚未成功,同志仍需努力啊。

    请查看下一篇《《30天自制操作系统》笔记(10)——定时器

  • 相关阅读:
    vue-awesome-swiper
    兼容各浏览器到达顶部
    正则表达式
    设置当前窗口url中param的值
    IE8不支持的部分css3属性处理
    WebGL中的函数及内置属性介绍
    WebGL着色器
    cookie的存储
    如何处理一段字符串数据
    MUI框架
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/OS-in-30-days-09-draw-windows.html
Copyright © 2020-2023  润新知