• 《30天自制操作系统》10_day_学习笔记


    harib07a:
      整理内存管理函数:memman_alloc和memman_free能够以最小1字节进行内存管理,但时间久了后,容易产生外部碎片;为此,笔者编写了一些以0x1000字节为单位进行内存分配和释放的函数,它们会把指定的内存大小按照0x1000字节为单位向上舍入(roundup),0x1000大小正好是4KB.
      1、向下舍入:以0x1000为单位向下舍入
        0x1234_5678 & 0xffff_f000 = 0x1234_5000;
        i = i & 0xffff_f000 ;
      2、向上舍入:以0x1000为单位向上舍入
        i = (i + 0xfff) & 0xffff_f000

    unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size) {
        unsigned int a;
        //以0x1000为单位向上舍入的方式进行内存分配
        size = (size + 0xfff) & 0xfffff000;
        a = memman_alloc(man, size);
        return a;
    }
    int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size) {
        int i;
        //以0x1000为单位向上舍入的方式进行内存释放
        size = (size + 0xfff) & 0xfffff000;
        i = memman_free(man, addr, size);
        return i;
    }

    harib07b:
      图层的定义:下面我们来解决图层叠加的问题,编写一段程序,既适用于于鼠标的叠加,也适应于窗口的叠加!

    #define MAX_SHEETS        256
    //图层的整体大小:bxsize*byseze
    //vx0和vy0表示图层在画面上坐标的位置
    //col_inv表示透明色色号
    //height :表示图层的高度
    //flags : 用于存放图层的各种设定信息
    struct SHEET {               //图层的结构体
        unsigned char *buf;         //记录图层上描绘内容的结构体
        int bxsize, bysize, vx0, vy0, col_inv, height, flags;
    };
    /* 图层管理的结构体;实现图层的叠加 */
    struct SHTCTL {
        unsigned char *vram;        //VRAM地址
        int xsize, ysize, top;       //VRAM画面的大小;top:最上面图层的高度
        struct SHEET *sheets[MAX_SHEETS];//sheets0[]啊没找高度排序后的图层地址
        struct SHEET sheets0[MAX_SHEETS];//用于存放256个图层的信息
    };

      图层的处理:界面图层处理的函数都被封装在sheet.c中,关于图层的具体实现方法和原理请对照图片和注释看

      (注释已经很详细了,应该能看懂!代码有点长,我们折起来吧!)

       

    //sheet.c
    #include "bootpack.h"
    
    #define SHEET_USE        1
    struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize)
    {                                          //图层控制变量的初始化
        struct SHTCTL *ctl;
        int i;
                                               //分配图层管理空间
        ctl = (struct SHTCTL *) memman_alloc_4k(memman, sizeof (struct SHTCTL));
        if (ctl == 0) {                           //分配空间失败
            goto err;
        }
        ctl->vram = vram;                          //给控制变量赋值
        ctl->xsize = xsize;
        ctl->ysize = ysize;
        ctl->top = -1;                              /* 表示没有图层(0) */
        for (i = 0; i < MAX_SHEETS; i++) {
            ctl->sheets0[i].flags = 0;               /* 标记为未使用 */
        }
    err:
        return ctl;                                //初始化失败
    }
    
    struct SHEET *sheet_alloc(struct SHTCTL *ctl)
    {                                          //用于取得新生成的未使用的图层
        struct SHEET *sht;
        int i;
        for (i = 0; i < MAX_SHEETS; i++) {
            if (ctl->sheets0[i].flags == 0) {        //找到第一个未使用的图层
                sht = &ctl->sheets0[i];            //返回第一个未使用图层的地址
                sht->flags = SHEET_USE;            /* 要分配,标记为正在使用 */
                sht->height = -1;                 /* 隐藏,此时高度还没有设置 */
                return sht;                      //取得(分配)成功
            }
        }
        return 0;                        /* 没有空闲图层,分配失败 */
    }
    //设定图层缓冲区大小和透明色的函数
    void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv)
    {
        sht->buf = buf;
        sht->bxsize = xsize;
        sht->bysize = ysize;
        sht->col_inv = col_inv;
        return;
    }
    //设定底板高度的函数:
    void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height)
    {
        int h, old = sht->height;                    /* 存储设置前的高度信息 */
    
                                                /* 如果指定的高度过高或过低,则进行修正 */
        if (height > ctl->top + 1) {                  //需要设定的高度与目前图层最高高度比较
            height = ctl->top + 1;
        }
        if (height < -1) {
            height = -1;
        }
        sht->height = height;                        /* 设定高度 */
    
                                               /* 有新的高度了,对sheets[]重新排序 */
        if (old > height) {                        /* 比以前低 */
            if (height >= 0) {
                                               /* 把中间的往上提 */
                for (h = old; h > height; h--) {     //这里不断把当前高度低的图层,不断的向上赋值
                    ctl->sheets[h] = ctl->sheets[h - 1];
                    ctl->sheets[h]->height = h;
                }
                ctl->sheets[height] = sht;
            } else {                            /* 高度小于0时,表示图层没有初始化,隐藏 */
                if (ctl->top > old) {
                                               /* 把上面的降下来 */
                    for (h = old; h < ctl->top; h++) {//这里不断把当前高度图层,不断地向下赋值
                        ctl->sheets[h] = ctl->sheets[h + 1];
                        ctl->sheets[h]->height = h;
                    }
                }
                ctl->top--;                       /* 现实中的图层少了一个,最上面的图层高度下降 */
            }
            sheet_refresh(ctl);                    /* 按照新图层的信息重新绘制画面 */
        } else if (old < height) {                  /* 图层更新后的高度比以前高 */
            if (old >= 0) {
                                        /* 把中间的拉下去 */
                for (h = old; h < height; h++) {     //不断的把图层往前移动
                    ctl->sheets[h] = ctl->sheets[h + 1];
                    ctl->sheets[h]->height = h;
                }
                ctl->sheets[height] = sht;
            } else {                            /* 由隐藏状态转为显示状态 */
                                              /* 将已经在上面的提上来 */
                for (h = ctl->top; h >= height; h--) { //不断地把图层往后移动
                    ctl->sheets[h + 1] = ctl->sheets[h];
                    ctl->sheets[h + 1]->height = h + 1;
                }
                ctl->sheets[height] = sht;
                ctl->top++;                      /* 由于已显示的图层增加了1个,所以最上面的图层高度增加 */
            }
            sheet_refresh(ctl);                   /* 按照新图层信息重新描绘画面 */
        }
        return;
    }
    //图层刷新函数。
    //刷新原理:对于已经设定的高度的所有图层,从下往上,将透明以外的所有像素都复制到VRAM中,由于是从下往上复制,所以最后最上面的内容就留在了画面上。
    void sheet_refresh(struct SHTCTL *ctl)
    {
        int h, bx, by, vx, vy;
        unsigned char *buf, c, *vram = ctl->vram;
        struct SHEET *sht;
        for (h = 0; h <= ctl->top; h++) {           //从图层0 开始,向高图层一个一个走
            sht = ctl->sheets[h];                  //获得每一层的地址
            buf = sht->buf;                     //将该地址的内容放到VRAM缓冲区
            for (by = 0; by < sht->bysize; by++) {    //VRAM按照从左到右,从上到下的顺序,不断的更新VRAM缓存
                vy = sht->vy0 + by;
                for (bx = 0; bx < sht->bxsize; bx++) {
                    vx = sht->vx0 + bx;
                    c = buf[by * sht->bxsize + bx];
                    if (c != sht->col_inv) {
                        vram[vy * ctl->xsize + vx] = c;
                    }
                }
            }
        }
        return;
    }
    //功能:不改变图层的高度,只上下左右移动图层(滑动)
    void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
    {
        sht->vx0 = vx0;
        sht->vy0 = vy0;
        if (sht->height >= 0) {               /* 正在显示 */
            sheet_refresh(ctl);               /* 按照新图层的信息刷新页面 */
        }
        return;
    }
    //释放已经使用图层的内存的函数
    void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
    {
        if (sht->height >= 0) {
            sheet_updown(ctl, sht, -1);        /* 如果该图层正在显示,设置height=-1;隐藏 */
        }
        sht->flags = 0;                     /* FLAG=0表示该图层未使用 */
        return;
    }
    View Code

    harib07c:
      上一步我们实现了界面图层显示的叠加,但当我们MAKE RUN 发现实在是太慢了!接下来我们来提高图层叠加处理的速度!
      1、提高刷新的速度
      从void sheet_refresh(struct SHTCTL *ctl)中我们可以看到,只要鼠标动了,就会对整个界面进行刷新!然而我们只需让它刷新相关的部分。我们在函数sheet_refresh()添加一个条件函数IF,只刷新鼠标经过的部分。

    void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1){
        int h, bx, by, vx, vy;
        unsigned char *buf, c, *vram = ctl->vram;
        struct SHEET *sht;
        for (h = 0; h <= ctl->top; h++) {
            sht = ctl->sheets[h];
            buf = sht->buf;
            for (by = 0; by < sht->bysize; by++) {
                vy = sht->vy0 + by;
                for (bx = 0; bx < sht->bxsize; bx++) {
                    vx = sht->vx0 + bx;
                    if (vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1) {
                        c = buf[by * sht->bxsize + bx];
                        if (c != sht->col_inv) {
                            vram[vy * ctl->xsize + vx] = c;
                        }
                    }
                }
            }
        }
        return;
    }

      2、使用fefreshsub提高sheet_slide的速度

      //首先记住移动前的位置,在设定新的位置,最后只要重新描绘移动前和移动后的地方。
      sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
      sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);

      3、图层内文字显示问题
      当我们在图层上显示或者改写文字时,并不是改写图层的全部内容;但是我们却要每次重写64000个像素的内容,怎么办?我们重新来编写sheet_refresh()函数吧!把文字显示单独处理。
      注  意:指定的范围不是直接指定画面内的左边,而是以缓冲区的坐标来表示的。

    void sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1)
    {
        if (sht->height >= 0) { /* 如果正在显示,则按图层信息刷新 */
            sheet_refreshsub(ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1);
        }
        return;
    }

      同时也要对sheet_updown进行修改,当然改动的只有sheet_refresh(ctl):
      sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize);

    harib07c:
      make run一下,还是感觉有些别扭;我们看看还有那些可以改进的地方。我们看看上面的refreshsub()发现即使不写入像素,任然要对每一层的每一个像素进行判断。但是对于需要刷新以外的部分,这就做了很多无用功。因此我们做了一下改良(如下图)

       

    void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1){
        int h, bx, by, vx, vy, bx0, by0, bx1, by1;
        unsigned char *buf, c, *vram = ctl->vram;
        struct SHEET *sht;
        for (h = 0; h <= ctl->top; h++) {
            sht = ctl->sheets[h];
            buf = sht->buf;
            /* 使用vx0--vy1,对bx0--by1进行倒推得到图层中需要刷新的范围
                vx = sht->vx0 ; --> bx = vx - sht->vx0;  */
            bx0 = vx0 - sht->vx0;               //倒推得到需要刷新的范围(bx0,by0)(bx1,by1)
            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;
                    c = buf[by * sht->bxsize + bx];    //获得该像素出缓冲区的内容
                    if (c != sht->col_inv) {
                        vram[vy * ctl->xsize + vx] = c; //将该像素出缓冲区的内容给VRAM
                    }
                }
            }
        }
        return;
    }
  • 相关阅读:
    day28-描述符应用与类的装饰器
    MySQL-快速入门(8)存储过程、存储函数
    MySQL-快速入门(7)索引
    MySQL-快速入门(6)连接查询、子查询、正则表达式查询、数据的插入删除更新
    MySQL-快速入门(5)数据查询-常用关键字、分组查询、聚合函数
    MySQL-快速入门(4)MySQL函数
    MySQL-快速入门(3)运算符
    MySQL-快速入门(2)数据类型
    MySQL-快速入门(1)基本数据库、表操作语句
    MySql-Mysql技术内幕~SQL编程学习笔记(N)
  • 原文地址:https://www.cnblogs.com/pengfeiz/p/5798664.html
Copyright © 2020-2023  润新知