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中,关于图层的具体实现方法和原理请对照图片和注释看

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

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

//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 == ) {            //分配空间失败
goto err;
}
ctl->vram = vram;          //给控制变量赋值
ctl->xsize = xsize;
ctl->ysize = ysize;
ctl->top = -;         /* 表示没有图层(0) */
for (i = ; i < MAX_SHEETS; i++) {
ctl->sheets0[i].flags = ;       /* 标记为未使用 */
}
err:
return ctl;         //初始化失败
} struct SHEET *sheet_alloc(struct SHTCTL *ctl)
{             //用于取得新生成的未使用的图层
struct SHEET *sht;
int i;
for (i = ; i < MAX_SHEETS; i++) {
if (ctl->sheets0[i].flags == ) {    //找到第一个未使用的图层
sht = &ctl->sheets0[i];       //返回第一个未使用图层的地址
sht->flags = SHEET_USE;        /* 要分配,标记为正在使用 */
sht->height = -;         /* 隐藏,此时高度还没有设置 */
return sht;         //取得(分配)成功
}
}
return ;                      /* 没有空闲图层,分配失败 */
}
//设定图层缓冲区大小和透明色的函数
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 + ) {       //需要设定的高度与目前图层最高高度比较
height = ctl->top + ;
}
if (height < -) {
height = -;
}
sht->height = height;        /* 设定高度 */                /* 有新的高度了,对sheets[]重新排序 */
if (old > height) {         /* 比以前低 */
if (height >= ) {
               /* 把中间的往上提 */
for (h = old; h > height; h--) {     //这里不断把当前高度低的图层,不断的向上赋值
ctl->sheets[h] = ctl->sheets[h - ];
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 + ];
ctl->sheets[h]->height = h;
}
}
ctl->top--;           /* 现实中的图层少了一个,最上面的图层高度下降 */
}
sheet_refresh(ctl);        /* 按照新图层的信息重新绘制画面 */
} else if (old < height) {        /* 图层更新后的高度比以前高 */
if (old >= ) {
                        /* 把中间的拉下去 */
for (h = old; h < height; h++) {     //不断的把图层往前移动
ctl->sheets[h] = ctl->sheets[h + ];
ctl->sheets[h]->height = h;
}
ctl->sheets[height] = sht;
} else {             /* 由隐藏状态转为显示状态 */
              /* 将已经在上面的提上来 */
for (h = ctl->top; h >= height; h--) { //不断地把图层往后移动
ctl->sheets[h + ] = ctl->sheets[h];
ctl->sheets[h + ]->height = h + ;
}
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 = ; h <= ctl->top; h++) {    //从图层0 开始,向高图层一个一个走
sht = ctl->sheets[h];      //获得每一层的地址
buf = sht->buf;         //将该地址的内容放到VRAM缓冲区
for (by = ; by < sht->bysize; by++) { //VRAM按照从左到右,从上到下的顺序,不断的更新VRAM缓存
vy = sht->vy0 + by;
for (bx = ; 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 >= ) {       /* 正在显示 */
sheet_refresh(ctl);       /* 按照新图层的信息刷新页面 */
}
return;
}
//释放已经使用图层的内存的函数
void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
{
if (sht->height >= ) {
sheet_updown(ctl, sht, -);     /* 如果该图层正在显示,设置height=-1;隐藏 */
}
sht->flags = ;         /* FLAG=0表示该图层未使用 */
return;
}

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 = ; h <= ctl->top; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
for (by = ; by < sht->bysize; by++) {
vy = sht->vy0 + by;
for (bx = ; 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 >= ) { /* 如果正在显示,则按图层信息刷新 */
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()发现即使不写入像素,任然要对每一层的每一个像素进行判断。但是对于需要刷新以外的部分,这就做了很多无用功。因此我们做了一下改良(如下图)

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

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 = ; 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 < ) { bx0 = ; }            //第一种情况:刷新范围的一部分被其他图层遮盖;用于处理刷新范围在图层外侧的情况
if (by0 < ) { by0 = ; }
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;
}
05-02 06:15