8 消除闪烁(2)(harib08h)
怎么样才能让鼠标不再闪烁呢?闪烁现象是由于一会儿描绘一会儿消除造成的。所以说要想消除闪烁,就要在刷新窗口时避开鼠标所在的地方对VRAM进行写入处理。这好像挺难的,但不管怎样我们还是要努力一下。
而且如果这里做好了,刷新窗口时就不需要重绘鼠标了,这样速度也能相应提高。一想到这里,就充满了克服困难的勇气和动力!
■■■■■
这样也不行,那样也不行,左思右想之后我们决定采用下面这种方法。首先,开辟一块内存,大小和VRAM一样,我们先称之为map(地图)吧。至于为什么要叫做地图,我们马上就来讲解。
本次的bootpack.h和sheet.c程序的节选
struct SHTCTL {
unsigned char *vram, *map; /* 这里! */
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
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->map = (unsigned char *) memman_alloc_4k(memman, xsize * ysize);
if (ctl->map == 0) {
memman_free_4k(memman, (int) ctl, sizeof (struct SHTCTL));
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;
}
这块内存用来表示画面上的点是哪个图层的像素,所以它就相当于是图层的地图。
当刷新图层1的时候,如果一边看着这个map一边刷新的话,就不必担心图层1和图层2重叠的部分被覆盖了。
■■■■■
下面我们来写向map中写入1、2等图层号码的函数。
本次的sheet.c节选
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;
}
这个函数与以前的refreshsub函数基本一样,只是用色号代替了图层号码而已。代表图层号码的变量sid是“sheet lD1 ”的缩写。
1 ID是英文identification的缩写,意为“用来表示身份的证件以及某一身份所对应的证件号码或记号等”。
■■■■■
下面是sheet_refreshsub函数。我们对它进行改写,让它可以使用map。
本次的sheet.c节选
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];
buf = sht->buf;
sid = sht - ctl->sheets0;
/* 利用vx0~vy1,对bx0~by1进行倒推 */
(中略)
for (by = by0; by < by1; by++) {
vy = sht->vy0 + by;
for (bx = bx0; bx < bx1; bx++) {
vx = sht->vx0 + bx;
if (map[vy * ctl->xsize + vx] == sid) {
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
}
}
}
}
return;
}
今后程序会对照map内容来向VRAM中写入,所以有时没必要从下面开始一直刷新到最上面一层,因此不仅要能指定h0,也要可以指定h1。
■■■■■
现在我们来修改调用了sheet_refreshsub的3个函数,先从较短的2个入手吧。
本次的sheet.c节选
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,
sht->height, sht->height);
}
return;
}
void sheet_slide(struct SHEET *sht, int vx0, int vy0)
{
struct SHTCTL *ctl = sht->ctl;
int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
sht->vx0 = vx0;
sht->vy0 = vy0;
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);
}
return;
}
在sheet_refresh函数里,由于图层的上下关系没有改变,所以不需要重新进行refreshmap的处理。实际上,我们有时候要把透明的地方变成不透明的,或者反过来要把不透明的地方变成透明的,遇到这些情况就必须重新编写map了,不过这里的sheet_refreshrefresh函数没有考虑这些情况。如果需要实现这样的功能,就要再编写其他的函数。
另外,在sheet_refresh函数里,需要刷新的图层只有一张,所以速度应该比较快。
在sheet_slide函数里,首先重写map,分别对应移动前后的图层,然后调用sheet_refreshsub函数。在移动前的地方,只针对上层图层移走之后而露出的下层图层进行重绘就可以了。在移动目的地处仅重绘了一张移动过去的图层。
■■■■■
最后是sheet_updown函数。
本次的sheet.c节选
void sheet_updown(struct SHEET *sht, int height)
{
(中略)
/* 下面主要是对sheets[]进行重新排列 */
if (old > height) { /* 比以前低 */
if (height >= 0) {
/* 中间的图层也提高一层 */
(中略)
sheet_refreshmap(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, height + 1, old);
} else { /* 隐藏 */
(中略)
sheet_refreshmap(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, 0, old - 1);
}
} else if (old < height) { /* 比以前高 */
(中略)
sheet_refreshmap(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize,
height);
sheet_refreshsub(ctl, sht->vx0, sht->vy0, sht->vx0 + sht->bxsize, sht->vy0 + sht->bysize,
height, height);
}
return;
}
在调用sheet_refreshsub函数之前,先执行sheet_refreshmap来重做map。
■■■■■
通过这些修改,闪烁现象真能消失吗?速度会变快吗?
感觉速度好像稍稍变快了些,而且鼠标不再闪烁了。我们成功了!
即使这样,鼠标也不闪啦。
哎呀,不知不觉居然都已经这么晚了,今天我们就到这里吧,明天见!