Day10
今天在稍微修改了一下内存管理之后,进入了处理图像叠加的问题上。
一些感想
按页(4KB)分配内存
虽然我们可能会多分配出去一些内存,但是这样可以有效减少内存碎片。
那么,这就涉及到了一个向上取整的问题。代码其实很简单
size = (size + 0xfff) & 0xfffff000;
相当于加上0xfff后再下取整,很好理解吧)
逐渐进化的叠加处理
为了绘制不同的窗口、图像,我们需要把这些图形放到不同的图层里头,不同的图层会有不同的高度(z轴),在绘制时候从下往上逐渐绘制即可。
具体的各种函数放到下面,现在来看一下,在每一次迭代中,图层是怎么重新绘制的
- 刷新时,重绘所有图层
这是最暴力的写法,需要把所有图层所有地方都要画一遍。可想而知,没有很好地处理重叠造成的计算浪费。 - 刷新时,重绘改变部分
例如鼠标移动,我们只需要重绘前后位置即可,这样可以节省很多像素的开销。 - 更改逻辑
在第2个版本中,作者会穷举一个窗口的所有像素,然后检查这个像素是不是在另一个窗口中。
我们可以这么优化:先计算出重叠部分,然后直接绘制这一个窗口就行了。
今天的代码比较多,但是技术含量不太高)
内存管理(续)
harib07a
我们对外分配内存的时候,按页分配内存,然后这个函数再使用我们之前写的分配内存函数
unsigned int memman_alloc_4k(struct MEMMAN *man, unsigned int size)
{
unsigned int a;
size = (size + 0xfff) & 0xfffff000;
a = memman_alloc(man, size);
return a;
}
int memman_free_4k(struct MEMMAN *man, unsigned int addr, unsigned int size)
{
int i;
size = (size + 0xfff) & 0xfffff000;
i = memman_free(man, addr, size);
return i;
}
叠加处理
harib07b
这里,我们需要引入图层的概念。因此,要引入图层的定义,接着是管理程序管理图层。
之后是管理图层的方法:初始化、申请、设定、调整高度、刷新显示屏、移动图层、释放图层函数。
一个个来
图层和图层管理的定义:
#define MAX_SHEETS 256
struct SHEET {
unsigned char *buf;
int bxsize, bysize, vx0, vy0, col_inv, height, flags;
};
struct SHTCTL {
unsigned char *vram;
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->vram = vram;
ctl->xsize = xsize;
ctl->ysize = ysize;
ctl->top = -1; // 1个sheet都没有
for(i = 0; i < MAX_SHEETS; ++i)
{
ctl->sheets0[i].flags = 0; // 标记为未使用
}
err:
return ctl;
}
给管理结构体分配内存。
申请:
#define SHEET_USE 1
struct SHEET* sheet_alloc(struct SHTCTL *ctl)
{
struct SHEET *sht;
int i;
for(i = 0; i < MAX_SHEETS; ++i)
{
if(ctl->sheets0[i].flags == 0)
{
sht = &ctl->sheets0[i];
sht->flags = SHEET_USE; // 标记为正在使用
sht->height = -1; // 隐藏
return sht;
}
}
return 0; // 所有SHEET都处于正在使用状态
}
设定:
void sheet_setbuf(struct SHEET *sht, unsigned char *buf, int xsize, int ysize, int col_inv)
{
sht->buf = buf;
sht->bxsize = xsize;
sht->bysize = ysize;
sht->col_inv = col_inv;
}
调整图层,其实是把一个图层改变高度后,插入到已经有序的图层之中。
void sheet_updown(struct SHTCTL *ctl, struct SHEET *sht, int height)
{
int h, old = sht->height; // 存储设置前的高度信息
// 如果指定的高度过高或过低,则进行修正
if(height > ctl->top + 1)
{
height = ctl->top + 1;
}
if(height < -1)
{
height = -1;
}
sht->height = height; //设定高度
// 下面主要是进行sheets[]的重新排列
if(old > height) // 比以前低
{
if(height >= 0)
{
// 把中间的往上提
for(h = old; h > height; --h)
{
ctl->sheets[h] = ctl->sheets[h - 1];
ctl->sheets[h]->height = h;
}
ctl->sheets[height] = sht;
}
else // 隐藏
{
if(ctl->top > old)
{
// 把上面的降下来
for(h = old; h < ctl->top; ++h)
{
ctl->sheets[h] = ctl->sheets[h + 1];
ctl->sheets[h]->height = h;
}
}
ctl->top--; // 由于显示中的图层减少了一个,所以最上面的图层高度下降
}
sheet_refresh(ctl); // 按新图层的信息重新绘制画面
}
else if(old < height) // 比以前高
{
if(old >= 0)
{
// 把中间的拉下去
for(h = old; h < height; ++h)
{
ctl->sheets[h] = ctl->sheets[h + 1];
ctl->sheets[h]->height = h;
}
ctl->sheets[height] = sht;
}
else // 由隐藏状态转为显示状态
{
// 将已在上面的提上来
for(h = ctl->top; h >= height; --h)
{
ctl->sheets[h + 1] = ctl->sheets[h];
ctl->sheets[h + 1]->height = h;
}
ctl->sheets[height] = sht;
ctl->top++; // 由于已显示的图层增加了1个,所以最上面的图层高度叠加
}
sheet_refresh(ctl); // 按新图层信息重新绘制
}
}
刷新屏幕:
void sheet_refresh(struct SHTCTL *ctl)
{
int h, bx, by, vx, vy;
unsigned char *buf, c, *vram = ctl->vram;
struct SHEET *sht;
for(h = 0; h <= ctl->top; ++h)
{
sht = ctl->sheets[h];
buf = sht->buf;
for(by = 0; by < sht->bysize; ++by)
{
vy = sht->vy0 + by;
for(bx = 0; bx < sht->bxsize; ++bx)
{
vx = sht->vx0 + bx;
c = buf[by * sht->bxsize + bx];
if(c != sht->col_inv)
{
vram[vy * ctl->xsize + vx] = c;
}
}
}
}
}
非常朴素的办法。。
移动图层:
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
sht->vx0 = vx0;
sht->vy0 = vy0;
if(sht->height >= 0) // 如果正在显示
{
sheet_refresh(ctl); // 按新图层的信息刷新画面
}
}
释放图层:
void sheet_free(struct SHTCTL *ctl, struct SHEET *sht)
{
if(sht->height >= 0)
{
sheet_updown(ctl, sht, -1); // 如果处于显示状态,则先设定为隐藏
}
sht->flags = 0; // “未使用”标志
}
之后是很长的。。对主函数的修改,这里就不放上来了,大体上除了SHTCTL的初始化之外,就是鼠标在移动的时候调用重绘函数即可。
提高叠加处理速度(1)
harib07c
原理已讲,我们只重新画改变的地方看看。
void sheet_refreshsub(struct SHTCTL *ctl, int vx0, int vy0, int vx1, int vy1)
{
int h, bx, by, vx, vy;
unsigned char *buf, c, *vram = ctl->vram;
struct SHEET *sht;
for(h = 0; h <= ctl->top; ++h)
{
sht = ctl->sheets[h];
buf = sht->buf;
for(by = 0; by < sht->bysize; ++by)
{
vy = sht->vy0 + by;
for(bx = 0; bx < sht->bxsize; ++bx)
{
vx = sht->vx0 + bx;
if(vx0 <= vx && vx < vx1 && vy0 <= vy && vy < vy1)
{
c = buf[by * sht->bxsize + bx];
if(c != sht->col_inv)
{
vram[vy * ctl->xsize + vx] = c;
}
}
}
}
}
}
然后修改slide:
void sheet_slide(struct SHTCTL *ctl, struct SHEET *sht, int vx0, int vy0)
{
int old_vx0 = sht->vx0, old_vy0 = sht->vy0;
sht->vx0 = vx0;
sht->vy0 = vy0;
if(sht->height >= 0) // 如果正在显示,则按新图层的信息刷新画面
{
sheet_refreshsub(ctl, old_vx0, old_vy0, old_vx0 + sht->bxsize, old_vy0 + sht->bysize);
sheet_refreshsub(ctl, vx0, vy0, vx0 + sht->bxsize, vy0 + sht->bysize);
}
}
然后refresh,这样就不需要刷新全屏了:
void sheet_refresh(struct SHTCTL *ctl, struct SHEET *sht, int bx0, int by0, int bx1, int by1)
{
if (sht->height >= 0) // 如果正在显示,则按新图层的信息刷新画面
{
sheet_refreshsub(ctl, sht->vx0 + bx0, sht->vy0 + by0, sht->vx0 + bx1, sht->vy0 + by1);
}
return;
}
然后updown也要改。。主函数也要改。这里就不写了。。
提高叠加处理速度(2)
harib07d
我们改进一下,直接算出重叠部分
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;
for(h = 0; h <= ctl->top; ++h)
{
sht = ctl->sheets[h];
buf = sht->buf;
// 使用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;
c = buf[by * sht->bxsize + bx];
if(c != sht->col_inv)
{
vram[vy * ctl->xsize + vx] = c;
}
}
}
}
}
emmm今天基本上都在讲绘制了,抛开代码量,本身逻辑还是挺好懂的。。