11.8 消除闪烁(2)(harib08h)

2023-04-23,,

ps:看书比较急,有错误的地方欢迎指正,不细致的地方我会持续的修改

11.8 消除闪烁(2)(harib08h)

11.7 消除闪烁(1)(harib08g)存在的问题: 鼠标放在计时器上会有 闪烁产生原因:会先绘制计时器,再绘制鼠标

书上的解决办法:

创造一个类VRAM,存储 图层号码,用于绘制。优点:似乎也可以同时解决点击问题。缺点:占内存,变复杂

另一种解决办法:

sheet_refresh进行改造,若指定图层上方有与指定图层相交的图层,则不再重绘 相交的区域

需考虑:1.如何判断其上有要显示的图层

struct SHEET *sheet_havehigher(struct SHEET *sht)//判断sht图层上是否有更高的图层
{
struct SHTCTL *ctl;
ctl=sht->ctl;
int i;
i=sht->height;
for (i ; i < MAX_SHEETS; i++) {
if (ctl->sheets0[i].flags == 1) {
return 1; //有
}
}
return 0;//无
}*/ void sheet_refresh(struct SHEET *sht, int bx0, int by0, int bx1, int by1)
{
if (sht->height >= 0) {
if(sheet_havehigher(sht)==0){/*这里!*/
sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height);
}else{/*这里!*/
sheet_refreshsub(sht->ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1, sht->height+1);
}
}
return;
}

上面的代码 sheet_havehigher用于判断指定图层上方有没有图层,sheet_refresh用于重绘

但上面的代码并没有解决闪烁问题,反而出现以下问题

数字不会变化,上方的数字也没有变化,只有鼠标滑过其上方才会重绘

上方代码的不足:

我们应该判断指定图层的上方指定区域内有没有比他更高的图层,且是此图层刷新后也需要刷新的,但这里却错误的判断了图层,即也许指定图层的绘制区域在中间,但比他图层高的鼠标在角落,二者并没有相交,也不会绘制指定区域。

结论:单纯利用图层高度无法判断,无法判断遮罩

疑问

在之前对sheet_refreshsub的优化中,似乎已经处理了重叠时的重新描画问题,为什么此处还是因为重绘高速计数器所在的层出现了问题

以下对书上的解决办法进行说明

先对 bootpack.h和sheet.c进行修改,增加map (书P214)

sheet_refreshmap

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,buf经过多次修改(覆盖),最终buf中的数据即为屏幕上所显示的图层的图层号

sheet_refreshsub

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的范围超出了画面则修正 */
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 <= h1; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
sid = sht - ctl->sheets0;
/* 利用vx0~vy1,对bx0~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++) {
vx = sht->vx0 + bx;
if (map[vy * ctl->xsize + vx] == sid) {
/* 如果该层的图层号码与map中的相同,则显示 */
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
}
}
}
}
return;
}

sheet_refreshsub的作用:

根据map中的信息,来绘制

注意:形参有所优化,增加h1,因为有时不需要刷新到最上层,目前不理解在调用函数时是怎么确定h2的,另外,如书中所言,此时未考虑透明与不透明之间的转换

sheet_updown的优化也较为简单,看书P218即可

总结:

产生闪烁的原因,在第一次是因为刷新时会按顺序连指定图层的下方的图层也一起进行刷新,而在第二次是因为会刷新指定图层上方的也需刷新的图层(如鼠标),这样两次刷新会产生闪烁。第一次的解决方案,为缩小从下到上刷新的范围,即之刷新指定图层以上,但这并不能解决第二次的问题。由此产生第二次的解决方案,即书上的方案,另外存储屏幕上显示画面是由哪个图层提供的信息。这就好比,以前我们是用很多张大小不一的纸张叠起来,从一面来看画面,而现在变成了,用拼图(图层的一部分)拼出整个画面。从这个角度,我在上面的疑问中提到的“如何判断其上有要显示的图层”,其解决办法就是书中使用的map,那么还有其他方法吗?不增加map可以做到吗?或者不像map这样记录的过多?现在暂时想不到其他解决方案。

11.8 消除闪烁(2)(harib08h)的相关教程结束。

《11.8 消除闪烁(2)(harib08h).doc》

下载本文的Word格式文档,以方便收藏与打印。