MacOS环境-手写操作系统-18-图片分层技术

简介: MacOS环境-手写操作系统-18-图片分层技术

图片分层技术

1.简介

在前面的章节中 我们完成了鼠标的绘制 但存在一个严重的问题是


当鼠标移动的时候 鼠标下方的窗口或图案会因为内核重绘鼠标而被擦除掉


解决这个问题的办法是 每当鼠标移动时 我们先把底下的图案绘制一遍


然后把鼠标在移动后的新坐标处再绘制一遍


由此 我们引出一个叫图层的概念 鼠标下面的背景是一个图层 鼠标自己处于一个图层 图层间有高度差别


作为背景的图层 它的高度就要比鼠标所在的图层低 因此内核每次重绘时 先绘制高度低的图层 再绘制高度高的图层


我们使用的操作系统 用鼠标挪动窗口时 窗口底部的图案不受影响 就是使用了图层分层技术


基本原理如下图的:

2.代码

有了基本原理后,我们可以编码实现了,首先定义图层的数据结构


struct SHEET {
    unsigned char *buf;
    int bxsize, bysize, vx0, vy0, col_inv, height, flags;
};


buf对应的是窗口的像素信息


bxsize, bysize 是窗口的长和高


vx0, vy0是窗口移动后的左上角坐标


col_inv 用来表示窗口中不显示的部分


height表示图层的高度


flags用来表示图层状态


这里需要解释一下的变量是col_inv


我们看看用来表示鼠标的buf:

static char cursor[16][16] = {
        "**************..",
        "*OOOOOOOOOOO*...",
        "*OOOOOOOOOO*....",
        "*OOOOOOOOO*.....",
        "*OOOOOOOO*......",
        "*OOOOOOO*.......",
        "*OOOOOOO*.......",
        "*OOOOOOOO*......",
        "*OOOO**OOO*.....",
        "*OOO*..*OOO*....",
        "*OO*....*OOO*...",
        "*O*......*OOO*..",
        "**........*OOO*.",
        "*..........*OOO*",
        "............*OO*",
        ".............***"
    };

上面的数组对应的就是SHEET结构中的buf, 我们绘制鼠标时


是按照数组中的元素对应到像素点的


如果某个元素的值是*那么对应的像素点是黑色


如果元素的值是0 那么对应像素的颜色就是白色


如果元素的值是 . 那么这些点对应的像素颜色不做任何改变


这样的话 下面图层的相关图案就可以“透视”出来


这些点对应的值就是SHEET中的col_inv


有了图层后,我们还要定义图层管理器对象


#define MAX_SHEETS  256
struct SHTCTL {
    unsigned char *vram;
    int xsize, ysize, top;
    struct SHEET *sheets[MAX_SHEETS];
    struct SHEET sheets0[MAX_SHEETS];
};
#define SIZE_OF_SHEET  32
#define SIZE_OF_SHTCTL 9232


其中vram 对应的是显存地址 也就是0xa0000, xsize, ysize 代表整个显示界面的宽和高


当前系统显示界面的大小是320 * 320


top 表示当前要显示几个图层


sheets0 是用来存储图层对象的数组


sheets 是指针数组 用来指向下面图层数组中的对应图层对象


根据图层数据结构 定义用来操作他们的相关函数接口:


所有代码都定义在文件win_sheet.h中


struct SHEET *sheet_alloc(struct SHTCTL *ctl);
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram,
  int xsize, int ysize);
void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize,
    int col_inv);
void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height);
int sheet_refresh(struct SHTCTL *ctl);
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0);


我们继续看看相关代码的实现

#include "mem_util.h"
#include "win_sheet.h"

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, SIZE_OF_SHTCTL);
    if (ctl == 0) {
        return 0;
    }

    ctl->vram = vram;
    ctl->xsize = xsize;
    ctl->ysize = ysize;
    ctl->top = -1;
    for (i = 0; i < MAX_SHEETS; i++) {
        ctl->sheets0[i].flags = 0;
    }

    return ctl;
}

#define SHEET_USE  1
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];
            ctl->sheets[i] = sht;
            sht->flags = SHEET_USE;
            sht->height = -1;
            return sht;
        }
    }

    return 0;
}

上面是两个初始化函数 分别初始化图层管理器和图层对象


在这两个函数中 使用到一个来自内存管理器的接口memman_alloc_4k


顾名思义 就是向管理器申请一块大小为4k整数倍的内存


即使你只使用1字节 分配到的内存大小仍然是4k


一下子分配4k是为了防止内存碎片化过于严重 以造成浪费


我们看看memman_alloc_4k的实现,该函数的代码在文件mem_util.c中:


unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size) {
    unsigned int a;
    size = (size + 0xfff) & 0xfffff000;
    a = memman_alloc(man, size);
    return a;
}


它的实现逻辑是这样的 4k对应的16进制数是0x1000


如果你申请大小是1字节 那么 size + 0xfff 就等于是 0x1000


跟0xfffff000 做与操作后 结果任然是0x1000


如果你申请的内存大小是 0xfff 也就是差1字节到4k 那么size + 0xfff的结果是0x1FFE


做与运算后结果也是0x1000


所以这种计算方法能够在你申请任何大小内存的情况下 将大小圆整到大于所申请内存的最小4k倍数


sheet_alloc用来分配一个图层对象给指定窗口 同时我们把图层的高设置成-1时 表示图层对应的窗口处于最小化状态


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;
}


sheet_setbuf将窗口对应的二位数组与图层对象关联起来


例如上面的数组cursor就是鼠标绘制数值 cursor数组将会跟通过上面的函数跟其图层对象关联起来


下面我们看的将是最为复杂的一个函数

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;

    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 {
            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++;
        }

        sheet_refresh(ctl);
    }

}

这个函数比较大 我们分块分析


总体来说sheet_updown是用来调整每个窗口图层的高度的


某个窗口被点击后 它会从底部冒到顶部 也就是原来被遮住看不到的部分会全部显示出来


这种情况就是up 如果窗口被其他拖过来的窗口给遮住 这种情况就是down


第一部分代码如下


int h, old = sht->height;
    if (height > ctl->top + 1) {
        height = ctl->top + 1;
    }
    if (height < -1) {
        height = -1;
    }
    sht->height = height;


如果是up 也就是传进来的参数height比图层对应的高度要高 那么就改变图层的高度值


如果调整的高度过大 比如当前只桌面上只有三个窗口(ctl->top == 3)


但你要把新增窗口设置成100 那么前面这段代码会把当前窗口的高度设置为4


如果窗口下降高度,例如当前有三个窗口图层:


sheet1{height = 1;}
sheet2{height = 2;}
sheet3{hegiht = 3;}


现在最高的窗口sheet3高度要降为最低,那么就把sheet1, sheet2分别往后挪一个位置,然后将sheet3 放到最上面:


sheet3{hegiht = 1;}
sheet1{height = 2;}
sheet2{height = 3;}


对应的代码


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 {
            ....
        }
    sheet_refresh(ctl);
 }


如果当前窗口最小话 那么该窗口对应的图层对象就从图层数组中去掉


例如sheet3的窗口最小化 那么图层数组由原来的


sheet1{height = 1;}
sheet2{height = 2;}
sheet3{hegiht = 3;}


变为


sheet1{height = 1;}
sheet2{height = 2;}


对应的代码


if (old > height) {
        if (height >= 0) {
           。。。。
        } else {
            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);
 }


sheet_refresh(ctl); 的作用是根据图层数组绘制图层,后面再详细分析


如果当窗口的高度变高 底层的窗口激活后要在最上头显示


例如有下面三个图层对应的窗口


sheet1{height = 1;}
sheet2{height = 2;}
sheet3{hegiht = 3;}



如果sheet1对应的窗口从最低位置转换到最前端显示,那么图层数组就做如下变得


sheet2{height = 1;}
sheet3{hegiht = 2;}
sheet1{height = 3;}


对应的逻辑代码


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 {
            。。。。
        }
        sheet_refresh(ctl);
    }


如果某个窗口原来处于最小化 现在要把它的高度设置为2


那么原来高度大于2的窗口 他们的高度就要相应的增加


然后再把空出来的图层位置让给新改变的图层


例如当前有三个窗口对应的图层如下


sheet1{height = 1;}
sheet2{height = 2;}
sheet3{hegiht = 3;}


新窗口对应的图层是sheet4,它的高度从-1


也就是最小化形态转为高度为2


那么就需要把原来高度为2以上的图层在图层数组中往后挪


并改变高度


sheet1{height = 1;}
sheet2{height = 3;}
sheet3{hegiht = 4;}


再把sheet4图层插入挪动后空出来的位置


sheet1{height = 1;}
sheet4{height = 2;}
sheet2{height = 3;}
sheet3{hegiht = 4;}


对应的逻辑代码


else if (old < height) {
        if (old >= 0) {
           。。。。。
        } 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++;
        }
        sheet_refresh(ctl);
    }


图层的绘制函数

int 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++) {

        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;
                c = buf[by * sht->bxsize + bx];
                if (c != sht->col_inv) {
                    vram[vy * ctl->xsize + vx] = c;
                }
            }
        }
    }

    return 0;
}

3.编译运行(这次比上次要多一些)

由于模块多起来了 而且是分开的


因此它们需要单独编译 然后再把这些的 .o 文件合并成一个模块


编译过程如下:


(我是在MAC下 当前版本是 MacOS BigSur)


先使用命令编译mem_util.c


再编译write_vga_desktop.c


i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o mem_util.o mem_util.c
i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o win_sheet.o win_sheet.c
i386-elf-gcc -m32 -fno-asynchronous-unwind-tables -s -c -o write_vga_desktop.o write_vga_desktop.c


编译后 用ld将其合并链接起来


i386-elf-ld -m elf_i386 -r write_vga_desktop.o mem_util.o win_sheet.o -o ckernel.o


链接后 对其进行反编译


./objconv -fnasm ckernel.o ckernel.asm


接着删除其中多余的部分


(好几千行我就不放出来了)


接着再配合kernel一起汇编


nasm -o kernel.bat kernel.asm


(如果出现错误 说跳转不能太长 记得添加 near 我当前环境没有报错 所以我就不处理了)


运行java 生成 system.img


相关文章
|
13小时前
|
Ubuntu Java iOS开发
MacOS环境-手写操作系统-00-开篇 简单成品介绍
MacOS环境-手写操作系统-00-开篇 简单成品介绍
20 14
|
13小时前
|
Ubuntu Java C语言
MacOS环境-手写操作系统-07-C语言绘制系统界面
MacOS环境-手写操作系统-07-C语言绘制系统界面
6 1
|
11小时前
|
编解码 C语言 iOS开发
MacOS环境-手写操作系统-27-修改分辨率
MacOS环境-手写操作系统-27-修改分辨率
6 0
|
12小时前
|
Java iOS开发 MacOS
MacOS环境-手写操作系统-20-图层叠加优化
文章介绍了通过优化图层叠加技术,解决鼠标移动导致大量窗口重绘、消耗CPU资源的问题。通过只重绘鼠标移动影响的区域,以及改进文字显示机制,显著提升了系统性能。修改后的代码包括sheet_refreshsub和sheet_slide函数,以及showString函数,实现了更高效的窗口更新和文字绘制。 摘要由CSDN通过智能技术生成
8 0
|
12小时前
|
存储 算法 C语言
MacOS环境-手写操作系统-15-内核管理 检测可用内存
MacOS环境-手写操作系统-15-内核管理 检测可用内存
8 0
|
11小时前
|
存储 iOS开发 MacOS
MacOS环境-手写操作系统-33-多任务多窗口
MacOS环境-手写操作系统-33-多任务多窗口
7 0
|
12小时前
|
Ubuntu Java iOS开发
MacOS环境-手写操作系统-19-编译自动化
MacOS环境-手写操作系统-19-编译自动化
6 0
|
12小时前
|
Java C语言 iOS开发
MacOS环境-手写操作系统-11-建立中断机制
本文详细介绍了如何为内核建立中断机制,涉及8259A中断控制器的初始化、中断信号的传递过程以及中断描述符表的设置。通过汇编和C语言代码展示了如何处理中断,特别是键盘和鼠标中断,最后给出了编译和运行的步骤。 摘要由CSDN通过智能技术生成
7 0
|
11小时前
|
小程序 iOS开发 MacOS
MacOS环境-手写操作系统-44-运行简单的程序
MacOS环境-手写操作系统-44-运行简单的程序
7 0
|
11小时前
|
编译器 API C语言
MacOS环境-手写操作系统-45-C语言开发应用程序
MacOS环境-手写操作系统-45-C语言开发应用程序
9 0