Day11
一些感想与问题
改来改去的。。
上一节刚定义好各种SHTCTL,这一节觉得用起来太麻烦了,又往里头加了点东西。。所有显示函数大改。。。emmmm失去耐心
画面外支持
首先是支持图层的部分到画面外,这样做很简单,只要我们在绘制的时候,对于图层边界以及显示边界取个min即可。
图层指定管理它的图层管理
听起来有点绕口。其实把图层管理理解为树根,图层理解为子节点的话,就是添加了子节点到树根的指针。这样做更方便对图层直接指定图层管理了。
应对重绘带来的闪烁
当我们重绘需要进行的比较快的时候,有可能各个叠加的图层之间还没画好。于是这样解决
- 重绘一定高度的图层
我们没必要从最底层开始重绘各个图层,我只需要画被改变的图层,以及其高度以上的图层即可。毕竟下面的都显示不出来。
但是其实这样只是避免了底层的闪烁。假设该图层不是最高层,还是会因为不断重绘造成该层和高层之间的闪烁。 - 设置每一个像素对应的图层表
我们对应每个像素是要由哪个图层来绘制。于是有更改的时候,先在表里写好每个像素对应的图层(这时不涉及绘制,不会有闪烁),重绘的时候,判断一下该像素应不应该被这个图层重绘即可。 - 有没有其它的改进策略?
看到作者这里用了一个map来预先储存要绘制的信息,我们也很容易想到双缓冲技术——其实这里也有点像,但是仔细看sheet_slide()这个函数的话,其实还是一层层的重绘,但是是因为每个位置只会对应一个重绘图层,不会造成闪烁。
以上的每一步,伴随的都是各种函数的大改,看起来会有点麻烦。。
鼠标显示问题
harib08a
其实是我们之前设置的问题,现在只要鼠标的左上角还在屏幕里就行。
if(mx > binfo->scrnx - 1)
{
mx = binfo->scrnx - 1;
}
if(my > binfo->scrny - 1)
{
my = binfo->scrny - 1;
}
但是我们只是改了位置限制,还没有改重绘函数
实现画面外的支持
harib08b
在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;
if(vx0 < 0) vx0 = 0;
if(vy0 < 0) vy0 = 0;
if(vx1 > ctl->xsize) vx1 = ctl->xsize;
if(vy1 > ctl->ysize) vy1 = ctl->ysize;
改的其实就这4个if
shtctl的指定省略
harib08c
为了使得我们更改图层的时候,能更方便地同时更改图层管理,我们在SHEET里加上了指向SHTCTL的指针。
所以,init、updown、refresh、slide、free都需要修改一下。。因为不用再指定shtctl了
代码就省略了吧。。没什么技术含量。
然后主函数里也可以顺便不用指定shtctl了。
显示窗口
harib08d
我们申请个图层来管理一个窗口,在buf里写上各种矩形,还有一个“X”:
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_FFFFFF, 1, 1, xsize - 2, 1 );
boxfill8(buf, xsize, COL8_C6C6C6, 0, 0, 0, ysize - 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_000000, xsize - 1, 0, xsize - 1, ysize - 1);
boxfill8(buf, xsize, COL8_C6C6C6, 2, 2, xsize - 3, ysize - 3);
boxfill8(buf, xsize, COL8_000084, 3, 3, xsize - 4, 20 );
boxfill8(buf, xsize, COL8_848484, 1, ysize - 2, xsize - 2, ysize - 2);
boxfill8(buf, xsize, COL8_000000, 0, ysize - 1, xsize - 1, ysize - 1);
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;
}
于是,我们在主函数里也进行了一些修改:申请图层、申请图层缓冲区、绘制图层、调整图层位置和高度。(代码略)
小实验
harib08e
如果把不同图层放到不同高度,就可以做到比如,鼠标被窗口覆盖的效果。
高速计数器
harib08f
原来是不断累加一个数啊。。之后把数绘制到窗口里。
方法本来很简单,但是会引出绘制时的闪烁问题,接下来两个标题都是来解决这个问题的。
消除闪烁(1)
harib08g
用的是之前说的第1个方法,为此我们又要改refresh_sub及其相关函数。。
对于其相关函数,我们只需要重绘对应图层高度及以上即可。
接下来要解决更高图层的闪烁
消除闪烁(2)
harib08h
引入了refresh_map:先把各个像素写好。
map的init就不写了,这里看一下map是怎么刷新的:
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;
if (buf[by * sht->bxsize + bx] != sht->col_inv) {
map[vy * ctl->xsize + vx] = sid;
}
}
}
}
return;
}
一个从低到高逐层往map里写入的过程。
那么,重绘函数就要先刷新map,然后再进行绘制啦。
看一下refresh_sub的判断:属于这个图层该绘制的,再绘制它
if (map[vy * ctl->xsize + vx] == sid) {
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
}
之后是refresh_slide:
把一个高度为h的图层从S移动到T,S位置需要从高度0开始重绘到h - 1(确切地说,map需要从0检测到top,而重绘只需要从0重绘到h-1),T位置需要从高度h开始重绘到top(确切地来说,map需要从h检测到top,而重绘只需要仅绘制h就行了)
(S、T不用重绘更高层的原因是:毕竟如果更高重叠了,我们也肯定用不上重绘,本来就在那里嘛)
if (sht->height >= 0) { // 如果正在显示,则按新图层的信息进行刷新
sheet_refreshmap(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0);
sheet_refreshmap(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height);
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize, 0, sht->height - 1);
sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize, sht->height, sht->height);
}
然后是sheet_updown中,当我们把图层提高或者降低之后,在refreshsub之前需要添加refreshmap。
这两天都在搞各种图形的绘制。。看着代码是真的长。。。。