【 *** 作系统】30天自制 *** 作系统--

【 *** 作系统】30天自制 *** 作系统--,第1张

        本章首先处理了一下鼠标在画面边缘的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便修复了。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/langs/713418.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存