本章首先处理了一下鼠标在画面边缘的bug,另外创建窗口,在窗口中显示内容(计数器累加),并修复了内容刷新的会发生闪烁的bug(我在使用成熟的嵌入式界面绘制软件emWin的时候,也会发生图层叠加,刷新不当,而导致显示的内容闪烁的情况,所以这类bug还是比较典型的,应当引以为戒)。
一 鼠标在界面边缘显示的bug修复在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;
/* 如果刷新的范围超过了图层的范围,则进行修正 */
if (vx0 < 0) { vx0 = 0; }
if (vy0 < 0) { vy0 = 0; }
if (vx1 > ctl->xsize) { vx1 = ctl->xsize; }
if (vy1 > ctl->ysize) { vy1 = ctl->ysize; }
/* 中略 */
}
这样处理,即可修复当鼠标拖动到屏幕范围之外时,显示异常的bug。
二 显示窗口基于目前的图层构架,绘制窗口的 *** 作呼之欲出。大致可以分为以下几个步骤:
【1】编写绘制窗口的函数make_window8:
其中包含绘制窗口右上角的X符号、窗口标题文字、窗口正文文字、窗口标题栏以及窗口正文栏。
【2】主函数中,先分配图层,并分配对应内存空间:
struct SHEET *sht_win;
unsigned char *buf_win;
sht_win = sheet_alloc(shtctl);
buf_win = (unsigned char *) memman_alloc_4k(memman, 160 * 68);
【3】再设置新建窗口图层的颜色以及透明度:
sheet_setbuf(sht_win, buf_win, 160, 68, -1);
【4】调用上面【1】中的绘制窗口函数来绘制窗口,并添加正文内容:
make_window8(buf_win, 160, 68, "window");
putfonts8_asc(buf_win, 160, 24, 28, COL8_000000, "Welcome to");
putfonts8_asc(buf_win, 160, 24, 44, COL8_000000, " Haribote-OS!");
【5】设置图层位置sheet_slide、设置图层高度sheet_updown(背景图层高度为0,鼠标图层高度为2,这个窗口图层处于中间位置):
sheet_slide(sht_win, 80, 72);
/*背景图层高度为0,鼠标图层高度为2,这个窗口图层处于中间位置*/
sheet_updown(sht_win, 1);
这样,便在主函数的初始化中,绘制了一个基础的窗口,当然目前的窗口还没有添加移动以及内容刷新的逻辑,后面继续。
三 图层叠加,刷新不当而导致显示内容闪烁的bug修复上面在初始化阶段创建了一个窗口,下面在主循环中,增加该窗口的使用,文中作者使用的是添加一个计数器,每个循环在窗口中累加显示:
void HariMain(void)
{
/* 初始化 */
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); //刷新窗口图层
/* 略 */
}
}
接下来需要修复两处BUG:
【1】第一处BUG显而易见,调用sheet_refresh(sht_win, 40, 28, 120, 44)进行刷新,会按照高度顺序依次刷新背景图层---->窗口图层---->鼠标图层,这边先刷窗口图层的话,就会发生闪烁的情况
顺着BUG的现象,就可以想到消除这种闪烁的办法,即调用sheet_refresh进行刷新的话,对其自身以及高度更高的图层进行刷新即可,对其高度更低的图层不予理会。
这边的处理是在sheet_refreshsub中加入一个h0参数,这个参数用来传递当前图层的高度,刷新即可以从当前图层到最高图层依次刷新,避免了低等级的图层刷新(当然,所有调用到sheet_refreshsub的地方均需修改):
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{
/* 中略 */
for (h = h0; h <= ctl->top; h++) {
/* 中略 */
}
}
修改之后运行结果如下:
【2】第二处BUG可以看到,当鼠标位置与累加器的刷新有重叠时,鼠标也会周期性地闪烁,原因在于刷新地过程,虽然不改变底层图层,但是对于上层的鼠标图层,还是存在一会儿绘制,一会儿消除的处理。
针对这个问题的修改就比上一个复杂了,核心思想是在刷新窗口时避开鼠标所在的地方对VRAM进行写入处理。具体的办法是开辟一块新内存map,大小和vram一样,这块内存用来表示画面上的点是哪个图层的像素,所以它就相当于是图层的地址,就是说把所有图层都平面化了:
/* 定义在结构体中 */
struct SHTCTL {
unsigned char *vram, *map;
int xsize, ysize, top;
struct SHEET *sheets[MAX_SHEETS];
struct SHEET sheets0[MAX_SHEETS];
};
/* 在初始化 shtctl 时分配内存 */
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;
}
当更新这个 map 的时候,把 sht - ctl->sheets0 这个地址当作图层的号码使用:
/**
* @brief 从 h0 层开始向上刷新图层颜色 map, 过滤 透明
*/
void sheet_refreshmap(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0)
{
int h, bx, by, vx, vy;
int 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;
}
下面是对 sheet_refreshsub 函数进行改写使之能使用图层绘制函数:
/**
* @brief 根据 颜色map(保证不重叠), 指定在 (h0, h1) 层, (vx0, vy0) 到 (vx1, vy1) 之间刷新像素
*/
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1, int h0, int h1)
{
int h, bx, by, vx, vy;
int bx0, by0, bx1, by1;
unsigned char *buf, c, *vram = ctl->vram, *map = ctl->map, sid;
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 <= h1; h++) {
sht = ctl->sheets[h];
buf = sht->buf;
sid = sht - ctl->sheets0;
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) { //最核心的一句判断!!!
vram[vy * ctl->xsize + vx] = buf[by * sht->bxsize + bx];
}
}
}
}
return;
}
上面函数中最核心的部分就是if (map[vy * ctl->xsize + vx] == sid)这一句判断,如下图所示,如果这时候要刷新窗口图层1中的内容,只需要刷新下图中 {1-2} 中的内容(即图中灰色部分)。【1】中解决的BUG保证了不刷新 {0-1} 中的内容,即图中的绿色部分;而这边【2】中加入了map逻辑,保证了不刷新 {2} 中的内容,即图中的白色部分。
相应的,调用了sheet_refreshsub的三个函数也要修改。
改完之后,今后程序会对照map内容来向VRAM中写入,所以有时没必要从下面开始一直刷新到最上面一层,因此不仅要能指定h0,也要可以指定h1。
这样,鼠标闪烁的BUG便修复了。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)