java开发系统内核:为窗口图形图形绘制提供API

更详细的讲解和代码调试演示过程,请参看视频
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;
}

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


java开发系统内核:为窗口图形图形绘制提供API_第1张图片
这里写图片描述

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

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

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()的作用是模拟生成随机数,上面的代码运行后,效果如下:


java开发系统内核:为窗口图形图形绘制提供API_第2张图片
这里写图片描述

由于每次画一个点,我们总要刷新窗口,这么做会导致窗口绘制效率的低下,因此我们再做一个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;
}

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


java开发系统内核:为窗口图形图形绘制提供API_第3张图片
这里写图片描述

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

更详细的讲解和代码调试演示过程,请参看视频
Linux kernel Hacker, 从零构建自己的内核

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


java开发系统内核:为窗口图形图形绘制提供API_第4张图片
这里写图片描述

你可能感兴趣的:(java开发系统内核:为窗口图形图形绘制提供API)