/* 在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倍。
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;isheets0[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;htop;h++){
ctl->sheets[h]=ctl->sheets[h+1];
ctl->sheets[h]->height=h;
}
}
ctl->top--;/* 正在显示的图层减少了一个,故最上面的高度也减少 */
}
sheet_refresh(ctl); /* 按新图层的信息重新绘制画面 */
}
else if(old=0){/* 中间的图层往下降一层 */
for(h=old;hsheets[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;
}
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_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;byvy0 + by;
for (bx=bx0;bx
第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;byvy0;
for(bx=bx0;bxvx0;
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()都需要进行修改,这里就不一一介绍了。
你可能感兴趣的:(算法)