30天自制操作系统(第10-11天)

10天 叠加处理

10.1 内存管理(续)

根据9.3节的分析, memman_alloc memman_free 能够以 1 字节为单位进行内存管理。当出现开辟或释放1GB内存时,需要循环2^30次,相当地浪费时间,而且在不断地开辟和释放过程中会出现碎片内存,导致内存的浪费。按照《深入理解linux内核》的介绍,内存页大小为4k。所以下面构建申请和释放4k大小的内存:
1. 申请内存的大小必须是4k的倍数,所以需要将申请的内存大小按0x1000字节向上舍入;
2. 释放内存时,也需要参考上述方法。
/* 在man中分配size的内存空间(向上舍入),空间必须是4k的倍数 */
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size){
	unsigned int a;
	size=(size+0xfff)&0xfffff000;// 计算存放size内存的最小4k倍数
	a=memman_alloc(man,size);
	return a;
}

/* 在man中释放addr开始的4k内存 */
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size){
	int i;
	size=(size+0xfff)&0xfffff000;
	i=memman_free(man, addr, size);
	return i;
}

计算舍入时,按照C语言中的方法,假设申请100k内存,每页内存有4k,计算需要多少页能放下100k内存?答案是(100+4-1)/4=(100+4-1)>>2=25。上述程序与C语言的做法类似,采取位运算比除法要快。命令是所有CPU命令中速度最快的命令之一,和除法命令相比其 执行速度要快10倍到100倍。

10.2 叠加处理

像电脑打开多个窗口一样,有些窗口会存在相交部分,会导致上面的窗口遮住部分下面窗口中的部分画面。思考一下,这个地方应该怎么处理呢?可以按照每层的图像进行显示。图层需要哪些信息呢?需要图层上所描画内容(一般用数组表示)、图像的位置(vx0,vy0)、图像的大小 bxsize*bysize、背景颜色 col_inv、图层高度height以及使用标识 flags,汇总得到了SHEET结构体。还需要创建一个 SHTCTL结构体,用于管理图层,sheets0记录各图层的信息,sheets为图层地址变量(下标为i表示高度为i的图层地址)
struct SHEET {/* 图层信息 */
        unsigned char *buf;/* 图像形状 */
        int bxsize, bysize, vx0, vy0, col_inv, height, flags;
}
#define MAX_SHEETS 256 /* 能够管理的最大图层数 */
struct SHTCTL {
        unsigned char *vram;/*  VRAM 的地址 */
        int xsize, ysize, top;/*  画面大小为xsize *ysize,top为最上面图层的高度(即共有多少个图层) */
        struct SHEET *sheets[MAX_SHEETS];
        struct SHEET sheets0[MAX_SHEETS];
};
初始化结构体:输入项为memman内存,*vram为VRAM地址,xsize、ysize为屏幕大小。
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; /*一个SHEET没都有 */
    for (i = 0; i < MAX_SHEETS; i++) {
        ctl->sheets0[i].flags = 0; /* 标记为未使用 */
    }
err:
    return ctl;
}

分配图层:从头开始检查所有图层,并返回第一个未被使用的图层地址

/* 在ctl中寻找一个未被使用的SHEET结构体,并返回该结构体的相关信息 */
#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];
			sht->flags=SHEET_USE;/* 标记成正在使用 */
			sht->height=-1;//隐藏
			return sht;
		}
	}
	return 0;/* 所有的SHEET都处于正在使用状态*/
}

设置图层信息:根据输入项对某一图层进行赋值

/* 对sht中的元素进行初始化 */
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;

图层移动和刷新:将图层集合ctl中某一图层sht高度变成height。假设图层sht的原高度为old,有以下4种情况。上述操作完成后,还需要用函数sheet_refresh刷新显示。

1.old>height && height != -1,表示以前的图层高度较高,举例以前在第2层现在变成第1层,所以需要将图层高度范围为[height, old-1]的变成[height+1, old];

2.old>height && height == -1,表示该图层被隐藏了,所以将图层高度范围[old+1, ctl->top]变成高度[old, ctl->top-1],ctl->top--;

3.old<=height && old>=0,表示以前的图层高度较低,举例将第3层变成第5层,所以需要将图层高度范围为[old+1, height]的图层变成[old, height-1];

4.old<=height && old==-1,表示图层sht以前是被隐藏的,现在变成了显示状态,所以需要将图层高度范围为[hieght, ctl->top]的图层变成[hieght+1, ctl->top+1],ctl->top++。

/* 将sht的图层顺序变成height,并更新排序 */
void sheet_updown(struct SHEET *sht, int height){
	struct SHTCTL *ctl=sht->ctl;
	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 {/* 隐藏 */
			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;
}

/* 逐层显示画面 */
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++) {
        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;
}

图层滑动:仅图层内上下左右移动,仅改变图层内部坐标,不改变height

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); /* 如果处于显示状态,则先设定为隐藏 */
    }
    sht->flags = 0; /* "未使用"标志 */
    return;
}

10.3 提高叠加处理速度(1

由于每次刷新,都涉及到所有图层,导致时间较长。能否只刷新图层中的部分画面呢?所以改进 sheet_refresh函数得 void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1) ,功能为刷新图层集合ctl中坐标(vx0, vy0)与(vx1-1, vy1-1)围成矩阵内的图像。
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;
}

sheet_refreshsub做了函数sheet_refresh的部分工作,所以在10.3节中用到函数sheet_refresh的地方都需要改进。函数sheet_refresh的功能为刷新图层sht中坐标(bx0, by0)与(bx1-1, by1-1)围成矩形范围内的所有图层图像。

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

10.4 提高叠加处理速度(2

改进 sheet_refreshsub函数,增加判断以减少循环次数:判断某一图层在 图层sht中坐标(bx0, by0)与(bx1-1, by1-1)围成矩形范围内是否有图像。即判断某一图层在某一区域内是否有图像,如果有则刷新相交部分的图像,如果没有则检查下一图层。
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;
    (中略)
        buf = sht->buf;
/*增加区间*/
        /*计算vx0在当前图层下的坐标(当前图层的(sht->vx0, sht->vy0)为原点)
          bx0、by0若小于0则超出当前图层的图像区间,则置为0;
          bx1、by1若大于当前图层的图像大小(超出部分没有图像,没有必要刷新),则置为图像的大小; */
        bx0=vx0-sht->vx0;
		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++) {/*修改*/
               (中略)
            }
        }
    }
    return;
}

11天 制作窗口

11.1 鼠标显示问题

为解决鼠标无法移到屏幕最右侧并隐藏的缺点,进行如下修改:
if (mx > binfo->scrnx - 16) mx = binfo->scrnx - 16;
if (my > binfo->scrny - 16) my = binfo->scrny - 16;
修改成
if (mx > binfo->scrnx - 1) mx = binfo->scrnx - 1;
if (my > binfo->scrny - 1) my = binfo->scrny - 1;
主要原因为:修改前mx、my为鼠标位置,其上限均为屏幕长宽-16(鼠标的字节大小为16*16),若想实现可以隐藏的功能,修改其上限即可。

11.2 实现画面外的支持

修改 sheet_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;
    /* 如果refresh的范围超出了画面则修正 */
    if (vx0 < 0) { vx0 = 0; }
    if (vy0 < 0) { vy0 = 0; }
    if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
    if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
    for (h = 0; h <= ctl->top; h++) {
        (中略)
    }
    return;
}

11.3 shtctl的指定省略

在SHEET结构体中,增加‘ struct SHTCTL *ctl; ’,以减少部分函数输入参数的数量。
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; /* 没有一张SHEET */
    for (i = 0; i < MAX_SHEETS; i++) {
        ctl->sheets0[i].flags = 0; /* 未使用标记 */
        ctl->sheets0[i].ctl = ctl; /* 记录所属*/ /* 这里! */
    }
err:
    return ctl;
}

void sheet_updown(struct SHEET *sht, int height){
    struct SHTCTL *ctl = sht->ctl;
    int h, old = sht->height; /* 将设置前的高度记录下来 */
    (中略)
}

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

void sheet_slide(struct SHEET *sht, int vx0, int vy0){
    int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
    sht->vx0 = vx0;
    sht->vy0 = vy0;
    if (sht->height >= 0) { /* 如果正在显示,则按新图层的信息进行刷新 */
        sheet_refreshsub(sht->ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
        sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);
    }
    return;
}

void sheet_free(struct SHEET *sht){
    if (sht->height >= 0) 
        sheet_updown(sht, -1); /* 如果正在显示,则先设置为隐藏 */
    sht->flags = 0; /* 未使用标记 */
    return;
}

11.4 显示窗口

通过修改函数 init_screen8,绘制一个xsize*ysize大小、名称为title的窗口。
void make_window8(unsigned char *buf, int xsize, int ysize, char *title){
	static char closebtn[14][16]={
		"OOOOOOOOOOOOOOO@",
		"OQQQQQQQQQQQQQ$@",
		"OQQQQQQQQQQQQQ$@",
		"OQQQ@@QQQQ@@QQ$@",
		"OQQQQ@@QQ@@QQQ$@",
		"OQQQQQ@@@@QQQQ$@",
		"OQQQQQQ@@QQQQQ$@",
		"OQQQQQ@@@@QQQQ$@",
		"OQQQQ@@QQ@@QQQ$@",
		"OQQQ@@QQQQ@@QQ$@",
		"OQQQQQQQQQQQQQ$@",
		"OQQQQQQQQQQQQQ$@",
		"O$$$$$$$$$$$$$$@",
		"@@@@@@@@@@@@@@@@"
	};
	int x, y;
	char c;
	/*窗口外矩形*/
	boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, xsize-1, 0);
	boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 1);
	boxfill8(buf, xsize, COL8_000000, xsize - 1, 0, xsize - 1, ysize - 1);
	boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);
	/*窗口外第二矩形*/
	boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, xsize - 2, 1 );
	boxfill8(buf, xsize, COL8_FFFFFF, 1, 1, 1, ysize - 2);
	boxfill8(buf, xsize, COL8_848484, xsize - 2, 1, xsize - 2, ysize - 2);
	boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);
	/*矩形内部图形*/
	boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);
	boxfill8(buf, xsize, COL8_000084, 3, 3, xsize - 4, 20 );
	
	putfonts8_asc(buf, xsize, 24, 4, COL8_FFFFFF, title);
	for(y=0;y<14;y++){
		for(x=0;x<16;x++){
			c=closebtn[y][x];
			if(c=='@')c=COL8_000000;
			else if(c=='$')c=COL8_848484;
			else if(c=='Q')c=COL8_C6C6C6;
			else c=COL8_FFFFFF;
			buf[(5+y)*xsize+(xsize-21+x)]=c;
		}
	}
	return;
}

11.5 高速计数器

据11.4节内容,创建一个告诉计数器的窗口,窗口名为‘counter’,并将计数结果打印到窗口中。
void HariMain(void)
{
    struct BOOTINFO *binfo = (struct BOOTINFO *) ADR_BOOTINFO;
    char s[40], keybuf[32], mousebuf[128];
    int mx, my, i;
    unsigned int memtotal, count = 0; /* 这里! */
    struct MOUSE_DEC mdec;
    struct MEMMAN *memman = (struct MEMMAN *) MEMMAN_ADDR;
    struct SHTCTL *shtctl;
    struct SHEET *sht_back, *sht_mouse, *sht_win;
    unsigned char *buf_back, buf_mouse[256], *buf_win;
(中略)
    init_palette();//初始化调色板
    shtctl = shtctl_init(memman, binfo->vram, binfo->scrnx, binfo->scrny);//在memman中创建SHTCTL结构体,并初始化
    
    /* 给背景、鼠标和窗口各分配一图层 */
    sht_back = sheet_alloc(shtctl);
    sht_mouse = sheet_alloc(shtctl);
    sht_win = sheet_alloc(shtctl);

    /* 给屏幕和窗口分配内存*/
    buf_back = (unsigned char *) memman_alloc_4k(memman, binfo->scrnx * binfo->scrny);
    buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 52); /* 这里! */

    /* 设置相关图层信息 */
    sheet_setbuf(sht_back, buf_back, binfo->scrnx, binfo->scrny, -1); /* 没有透明色 */
    sheet_setbuf(sht_mouse, buf_mouse, 16, 16, 99);
    sheet_setbuf(sht_win, buf_win, 160, 52, -1); /* 没有透明色 */ /* 这里! */

    /* 绘制屏幕、鼠标和窗口 */
    init_screen8(buf_back, binfo->scrnx, binfo->scrny);
    init_mouse_cursor8(buf_mouse, 99);
    make_window8(buf_win, 160, 52, "counter"); /* 这里! */

    /* 移动屏幕、鼠标和窗口的起始坐标 */
    sheet_slide(sht_back, 0, 0);
    mx = (binfo->scrnx - 16) / 2; /* 为使其处于画面中央位置,计算坐标 */
    my = (binfo->scrny - 28 - 16) / 2;
    sheet_slide(sht_mouse, mx, my);
    sheet_slide(sht_win, 80, 72);
    
    /* 变更屏幕、鼠标、窗口的图层高度 */
    sheet_updown(sht_back, 0);
    sheet_updown(sht_win, 1);
    sheet_updown(sht_mouse, 2);

    /* 打印相关信息 */
    sprintf(s, "(%3d, %3d)", mx, my);
    putfonts8_asc(buf_back, binfo->scrnx, 0, 0, COL8_FFFFFF, s);
    sprintf(s, "memory %dMB free : %dKB",
            memtotal / (1024 * 1024), memman_total(memman) / 1024);
    putfonts8_asc(buf_back, binfo->scrnx, 0, 32, COL8_FFFFFF, s);
    sheet_refresh(sht_back, 0, 0, binfo->scrnx, 48);
    for (;;) {
        count++; /* 从这里开始 */
        sprintf(s, "%010d", count);
        boxfill8(buf_win, 160, COL8_C6C6C6, 40, 28, 119, 43);
        putfonts8_asc(buf_win, 160, 40, 28, COL8_000000, s);
        sheet_refresh(sht_win, 40, 28, 120, 44); /* 到这里结束 */
        io_cli();
        if (fifo8_status(&keyfifo) + fifo8_status(&mousefifo) == 0) {
            io_sti(); /* 不做HLT */
        } else {
            (中略)
        }
    }
}

11.6 消除闪烁(1

窗口图层刷新是因为窗口的内容有变化,所以要在画面上显示变化后的新内容。基 本上来讲,可以认为其他图层的内容没有变化(如果其他图层的内容也变了,那么 应该会随后执行该图层的刷新)。所以增加sheet_refreshsub的输入参数h0,功能变成刷新图层集合ctl中且高度为h0及以上的各图层中,在坐标(vx0, vy0)与(vx1-1, vy1-1)围成矩形范围内的所有图像。
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{
    (中略)
    for (h = h0; h <= ctl->top; h++) {
    (中略)
    }
    return;
}

函数sheet_refresh中修改成sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1,sht->height);
函数 sheet_slide中修改成sheet_refreshsub(sht->ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);
函数sheet_updown中修改成 sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht- >vy0 + sht->bysize, height + 1);        sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht- >vy0 + sht->bysize, 0);        sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht- >bysize,height);

11.7 消除闪烁(2

创建一个大小与VRAM一样的内存,用于存储相应的像素点是哪个图层的,相当于图层的地图。
// vramVRAM的地址 xsize,ysize画面大小 top最上面图层的高度 sheets0存放图层信息 sheets地址变量
// map存放画面上的点是哪个图层的像素
struct SHTCTL{
	unsigned char *vram, *map;
	int xsize, ysize, top;
	struct SHEET *sheets[MAX_SHEETS];
	struct SHEET sheets0[MAX_SHEETS];
	
};

/* 在memman中创建SHTCTL结构体,并初始化 */
struct SHTCTL *shtctl_init(struct MEMMAN *memman, unsigned char *vram, int xsize, int ysize){
	int i;	struct SHTCTL *ctl;
	ctl=(struct SHTCTL *)memman_alloc_4k(memman, sizeof(struct SHTCTL));
	if (ctl == 0) {
		goto err;
	}
	/* 从这里开始 */
	ctl->map=(unsigned char*)memman_alloc_4k(memman, xsize*ysize);
	if(ctl->map==0){
		memman_free_4k(memman, (int)ctl, sizeof(struct SHTCTL));
		goto err;
	}
	/* 到这里结束 */
	(中略)
err:
	return ctl;
}

// 在ctl中,更新map中高度h0及以上的图层中坐标(vx0, vy0)和(vx1, vy1)围成矩形范围的图层号码
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0){
	int h, bx, by, vx, vy, bx0, by0, bx1, by1;
	unsigned char *buf, sid, *map=ctl->map;
	struct SHEET *sht;
	if(vx0<0)vx0=0;
	if(vy0<0)vy0=0;
	if(vx1>ctl->xsize)vx1=ctl->xsize;
	if(vy1>ctl->ysize)vy1=ctl->ysize;
	
	for(h=h0;h<=ctl->top;h++){
		sht=ctl->sheets[h];
		sid=sht-ctl->sheets0;/* 将进行了减法计算的地址作为图层号码使用 */
		buf=sht->buf;
		bx0 = vx0 - sht->vx0;
		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;// 不是透明色表明该位置有图形像素,更新对应map中的数据
				if (buf[by * sht->bxsize + bx] != sht->col_inv) {
					map[vy * ctl->xsize + vx] = sid;
				}
			}
		}
	}
	return;
}
/*刷新图层数组ctl中坐标(vx0, vy0)与(vx1-1, vy1-1)围成矩形内的、高度范围h0~h1的图层图像*/
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1){
	int h, bx, by, vx, vy, bx0, by0, bx1, by1;
	unsigned char *buf, *vram=ctl->vram,  *map = ctl->map, sid;
	struct SHEET *sht;
	/* 如果refresh的范围超出了画面则修正*/
	(中略)
	for(h=h0;h<=h1;h++){
		sht=ctl->sheets[h];
		sid=sht-ctl->sheets0;
		buf=sht->buf;
		(中略)
		for(by=by0;by<by1;by++){
			vy=by+sht->vy0;
			for(bx=bx0;bx<bx1;bx++){
				vx=bx+sht->vx0;
				if(map[vy*ctl->xsize+vx]==sid)/* 这里 */
					vram[vx+ctl->xsize*vy]=buf[by * sht->bxsize + bx];
			}
		}
	}
	return;
}

由于sheet_refreshsub函数增加了一项输入参数,所以函数sheet_refresh()、sheet_slide()、sheet_updown()都需要进行修改,这里就不一一介绍了。

  • 16
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值