您的位置:首页 > 编程语言 > Java开发

java开发系统内核:实现窗口图形绘制API

2017-08-16 17:36 701 查看
更详细的讲解和代码调试演示过程,请参看视频

Linux kernel Hacker, 从零构建自己的内核

为了今后能在系统上开发出更复杂绚丽的图形窗口应用程序,我们需要让内核添加图形图像绘制的API,这样应用程序才可以通过API绘制足够丰富多彩的窗口应用。

任何复杂的图像,都是通过绘制相应的像素点形成的,因此要提供图形绘制API的话,提供画点API是必不可免的。为此,我们在系统内核中提供一个编号为11的API,它的作用是在窗口的指定位置绘制一个点,API的相关配置如下:

EDX = 11

EBX = 窗口句柄

ESI = 点的X坐标

EDI = 点的Y坐标

EAX= 点的颜色

在kernel_api中添加相应的实现代码:

int* kernel_api(int edi, int esi, int ebp, int esp,
int ebx, int edx, int ecx, int eax) {

....
}else if (edx == 11){
sht = (struct SHEET*)ebx;
sht->buf[sht->bxsize * edi + esi] = eax;
sheet_refresh(shtctl, sht, esi, edi, esi + 1, edi + 1);
}
....
}


在api_call.asm中添加对应的汇编代码:

api_point:  ;void api_point(int win, int x, int y, int col)
push edi
push esi
push ebx
mov  edx, 11
mov  ebx, [esp+16]  ;win
mov  esi, [esp+20]  ;x
mov  edi, [esp+24]  ;y
mov  eax, [esp+28]  ;col
int  02Dh
pop  ebx
pop  esi
pop  edi
ret


然后我们在app.c中调用API,在一个窗口上绘制一个亮点:

void api_putchar(int c);
void api_putstr(char *s);
int  api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_point(int win, int x, int y, int col);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);

#define _A  214013LL
#define _B  2531011LL
int ran = 23;
int  rand() {
return ((ran = ran * _A + _B)>>16) & 0x7fff;
}

void main() {
char buf[160 * 100];
int win;
win = api_openwin(buf, 160, 100, -1, "star");
api_boxfilwin(win, 6, 26, 143, 93, 0);
api_point(win, 75, 59, 3);

return;
}


上面代码编译,把系统加载运行后,效果如下:



在窗口的中央有一个小黄点。

我们再把画点功能增强一下,多画几个随机点:

void api_putchar(int c);
void api_putstr(char *s);
int  api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_point(int win, int x, int y, int col);

#define _A  214013LL
#define _B  2531011LL
int ran = 23;
int  rand() {
return ((ran = ran * _A + _B)>>16) & 0x7fff;
}

void main() {
char buf[160 * 100];
int win;
win = api_openwin(buf, 150, 100, -1, "star");
api_boxfilwin(win, 6, 26, 143, 93, 0);
//api_point(win, 75, 59, 3);
int i = 0;
int x = 0;
int y = 0;
for (i = 0; i < 50; i++) {
x = rand() % 137 + 6;
y = rand() % 67 + 26;
api_point(win, x, y, 3);
}

return;
}


代码中的rand()的作用是模拟生成随机数,上面的代码运行后,效果如下:



由于每次画一个点,我们总要刷新窗口,这么做会导致窗口绘制效率的低下,因此我们再做一个API,让它编号为12,专门用来刷新窗口,这样程序可以把所有点都绘制完后,调用该API刷新窗口一次就可以了。API的配置如下:

EDX = 12

EBX = 窗口句柄

EAX = x0

ECX = y0

ESI = x1

EDI = y1

在kernel_api中的实现如下:

int* kernel_api(int edi, int esi, int ebp, int esp,
int ebx, int edx, int ecx, int eax) {
....
else if (edx == 11){
sht = (struct SHEET*)ebx;
sht->buf[sht->bxsize * edi + esi] = eax;
//        sheet_refresh(shtctl, sht, esi, edi, esi + 1, edi + 1);
}
else if (edx == 12) {
sht = (struct SHEET*)ebx;
sheet_refresh(shtctl, sht, eax, ecx, esi, edi);
}
....
}


对应的在api_call.asm中汇编代码实现为:

api_refreshwin:  ;void api_refreshwin(int win, int x0, int y0, int x1, int y1)
push  edi
push  esi
push  ebx
mov   edx, 12
mov   ebx, [esp+16] ; win
mov   eax, [esp+20] ;x0
mov   ecx, [esp+24] ;y0
mov   esi, [esp+28] ;x1
mov   edi, [esp+32] ;y1
int   02Dh
pop   ebx
pop   esi
pop   edi
ret


app.c中的实现也相应的做些修改:

void api_putchar(int c);
void api_putstr(char *s);
int  api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_point(int win, int x, int y, int col);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);

#define _A  214013LL
#define _B  2531011LL
int ran = 23;
int  rand() {
return ((ran = ran * _A + _B)>>16) & 0x7fff;
}

void main() {
char buf[160 * 100];
int win;
win = api_openwin(buf, 150, 100, -1, "star");
api_boxfilwin(win, 6, 26, 143, 93, 0);
//api_point(win, 75, 59, 3);
int i = 0;
int x = 0;
int y = 0;
for (i = 0; i < 50; i++) {
x = rand() % 137 + 6;
y = rand() % 67 + 26;
api_point(win, x, y, 3);
}

api_refreshwin(win, 6, 26, 154, 90);

return;
}


上面代码运行后,结果跟上次一样,唯一的不同在于,它的效率提高了很多。

最后,我们在添加一个画直线的API,它的编号为13,它的相关配置如下:

EDX = 13

EBX = 窗口句柄

EAX = x0

ECX = y0

ESI = x1

EDI = y1

EBP = 颜色

直线的绘制其实属于计算机图形学的内容,在这里我们直接给出代码,但不对它的原理做太多解释,只要你知道,调用下面的函数能在给定的两个坐标点间绘制直线就可以了,在write_vga_desktop.c中添加下面函数:

int api_linewin(struct SHEET *sht, int x0, int y0, int x1, int y1, int col) {
int i, x, y, len, dx, dy;
dx = x1 - x0;
dy = y1 - y0;
x = x0 << 10;
y = y0 << 10;

if (dx < 0) {
dx = -dx;
}
if (dy < 0) {
dy = -dy;
}

if (dx >= dy) {
len = dx + 1;
if (x0 > x1) {
dx = -1024;
} else {
dx = 1024;
}

if (y0 <= y1) {
dy = ((y1 - y0 + 1) << 10) / len;
} else {
dy = ((y1 - y0 - 1) << 10) / len;
}
} else {
len = dy + 1;
if (y0 > y1) {
dy = -1024;
} else {
dy = 1024;
}

if (x0 <= x1) {
dx = ((x1 - x0 + 1) << 10) / len;
} else {
dx = ((x1 - x0 - 1) << 10) / len;
}
}

for (i = 0; i < len; i++) {
sht->buf[(y >> 10) * sht->bxsize + (x>>10)] = col;
x += dx;
y += dy;
}
}

int* kernel_api(int edi, int esi, int ebp, int esp,
int ebx, int edx, int ecx, int eax) {
....
else if (edx == 13) {
sht = (struct SHEET*)ebx;
api_linewin(sht, eax, ecx, esi, edi, ebp);
}
....
}


在api_call.asm中添加相应的汇编代码:

api_linewin:   ;void api_linewin(int win, int x0, int y0, int x1, int y1, int col)
push edi
push esi
push ebp
push ebx
mov  edx, 13
mov  ebx, [esp+20] ;win
mov  eax, [esp+24] ;x0
mov  ecx, [esp+28] ;y0
mov  esi, [esp+32] ;x1
mov  edi, [esp+36] ;y1
mov  ebp, [esp+40] ;col
int  02Dh

pop  ebx
pop  ebp
pop  esi
pop  edi
ret


接着修改app.c的内容,在里面调用画直线API在窗口上绘制直线:

void api_putchar(int c);
void api_putstr(char *s);
int  api_openwin(char *buf, int xsiz, int ysiz, int col_inv, char *title);
void api_putstrwin(int win, int x, int y, int col, int len, char *str);
void api_boxfilwin(int win, int x0, int y0, int x1, int y1, int col);
void api_point(int win, int x, int y, int col);
void api_refreshwin(int win, int x0, int y0, int x1, int y1);
void api_linewin(int win, int x0, int y0, int x1, int y1, int col);

#define _A  214013LL
#define _B  2531011LL
int ran = 23;
int  rand() {
return ((ran = ran * _A + _B)>>16) & 0x7fff;
}

void main() {
char buf[160 * 100];
int win;
win = api_openwin(buf, 150, 100, -1, "star");
api_boxfilwin(win, 6, 26, 143, 93, 0);
//api_point(win, 75, 59, 3);
int i = 0;

for (i = 0; i < 8; i++) {
api_linewin(win, 8, 26, 77, i*9 + 26, i);
api_linewin(win, 88, 26, i*9+88, 89, i);
}

api_refreshwin(win, 6, 26, 154, 90);

return;
}


上面代码编译运行后,效果如下:



我们看到,直线并不是很‘直’,其中的锯齿比较严重,不过属于可以忍受的范围之内。

更详细的讲解和代码调试演示过程,请参看视频

Linux kernel Hacker, 从零构建自己的内核

更多技术信息,包括操作系统,编译器,面试算法,机器学习,人工智能,请关照我的公众号:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息