我們已經(jīng)有了系統(tǒng)桌面,但還缺少一個(gè)重要因素,那就是字體摔竿,沒有字體就無法有效的傳遞信息面粮,因此,本節(jié)继低,我們看看熬苍,系統(tǒng)的字體是如何繪制的。
字體的繪制跟其他圖形一樣袁翁,都是通過將指定位置的像素點(diǎn)設(shè)置成給定顏色而形成的最終圖形柴底,如下圖:
我們看到,要繪制給定的字母梦裂,我們可以把一塊圖形區(qū)域先全部染成白色,然后在將某個(gè)位置的像素點(diǎn)的顏色設(shè)置成黑色盖淡,那么年柠,字體就顯示出來了。如果我們把字體的大小限定在一個(gè)8*16的長(zhǎng)方形區(qū)域褪迟,那么我們?cè)谶@個(gè)區(qū)域內(nèi)冗恨,將特定位置的像素點(diǎn)設(shè)置成黑色,其他點(diǎn)設(shè)置成白色味赃,那么我們就可以得到一個(gè)白底黑色的字體:
依據(jù)上圖掀抹,如果我們把8*16區(qū)域當(dāng)做一個(gè)二維數(shù)組,白色的像素我們用0表示心俗,黑色像素我們用1表示傲武,那么上圖的字符A, 最頂層的一行全是白色,所以用8個(gè)0表示城榛,第二行揪利,8個(gè)像素中,中間兩個(gè)像素設(shè)置成黑色狠持,于是對(duì)應(yīng)的二進(jìn)制數(shù)就是000 11 000, 對(duì)應(yīng)的16進(jìn)制數(shù)就是0x18, 依次類推疟位,這樣對(duì)整個(gè)字符A, 我們就有可以設(shè)置一個(gè)對(duì)應(yīng)的數(shù)組:
static char fontA[16] = {
0x00, 0x18, 0x18, 0x18,0x18,0x24,0x24,0x24,
0x24, 0x7e, 0x42, 0x42,0x42, 0xe7, 0x00, 0x00
};
拿到字體數(shù)組后,繪制時(shí)喘垂,把數(shù)組中的每一個(gè)數(shù)值取出來甜刻,看該數(shù)值的二進(jìn)制形式中,哪一位設(shè)置成1正勒,那么就給對(duì)應(yīng)的像素賦予黑色得院,如果設(shè)置成0,就給對(duì)應(yīng)的像素設(shè)置成白色章贞,代碼如下:
void showFont8(char *vram, int xsize, int x, int y, char c, char* font) {
int i;
char d;
for (i = 0; i < 16; i++) {
d = font[i];
if ((d & 0x80) != 0) {vram[(y+i)*xsize + x + 0] = c;}
if ((d & 0x40) != 0) {vram[(y+i)*xsize + x + 1] = c;}
if ((d & 0x20) != 0) {vram[(y+i)*xsize + x + 2] = c;}
if ((d & 0x10) != 0) {vram[(y+i)*xsize + x + 3] = c;}
if ((d & 0x08) != 0) {vram[(y+i)*xsize + x + 4] = c;}
if ((d & 0x04) != 0) {vram[(y+i)*xsize + x + 5] = c;}
if ((d & 0x02) != 0) {vram[(y+i)*xsize + x + 6] = c;}
if ((d & 0x01) != 0) {vram[(y+i)*xsize + x + 7] = c;}
}
大家注意尿招,上面的代碼為什么要乘以 xsize 呢,xsize對(duì)應(yīng)屏幕的寬度,相隔一行的同一列上的像素點(diǎn)就谜,他們之間的距離正好是屏幕一行的寬度怪蔑,如果把屏幕看成是一個(gè)320*200 的二維數(shù)組screen[320][200], 那么點(diǎn)screen[1][1]和點(diǎn)screen[2][1] 之間的距離,就等于320.
在write_vga_desktop.c的主函數(shù)中丧荐,在for(;;)死循環(huán)前加入一句:
showFont8(vram, xsize, 20, 20, COL8_FFFFFF, fontA);
將上面的代碼結(jié)合進(jìn)write_vga_desktop.c缆瓣,編譯反編譯,結(jié)合到內(nèi)核后虹统,運(yùn)行效果如下:
我們看到弓坞,一個(gè)"A"字體就出現(xiàn)在屏幕做上方了。
####****增加更多字體
當(dāng)前我們只繪制了一個(gè)字符车荔,要繪制一系列字符的話渡冻,就需要相應(yīng)的數(shù)組,我們的做法是忧便,使用設(shè)計(jì)好的字體文件族吻,將字體文件轉(zhuǎn)換為二進(jìn)制數(shù)據(jù),先向大家展示這個(gè)字體文件的內(nèi)容樣式:
字體文件就是如上圖所示珠增,對(duì)每一個(gè)字符樣式進(jìn)行進(jìn)行設(shè)計(jì)超歌,一個(gè)“."就代表0,*代表1蒂教,同時(shí)巍举,我們會(huì)有一個(gè)附件工具程序叫makeFont,該程序?qū)⒋俗煮w文件讀入,然后轉(zhuǎn)換成對(duì)應(yīng)的16進(jìn)制數(shù)據(jù)凝垛。如下圖是makeFont工程的目錄懊悯,該目錄下有字體文件font.txt:
啟動(dòng)makeFont程序,并運(yùn)行之梦皮,完成后定枷,該工程目錄下會(huì)多出一個(gè)二進(jìn)制文件:fontData.inc:
該二進(jìn)制文件的局部?jī)?nèi)容如下:
systemFont:
db 00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H ,00H
db 00H ,00H ,070H ,088H ,04H ,054H ,054H ,04H ,04H ,054H ,024H ,088H ,070H ,00H ,00H ,00H
db 00H ,00H ,070H ,0f8H ,0fcH ,0acH ,0acH ,0fcH ,0fcH ,0acH ,0dcH ,0f8H ,070H ,00H ,00H ,00H
db 00H ,00H ,00H ,00H ,0d8H ,0fcH ,0fcH ,0fcH ,0f8H ,070H ,020H ,00H ,00H ,00H ,00H ,00H
db 00H ,00H ,00H ,00H ,020H ,070H ,0f8H ,0fcH ,0f8H ,070H ,020H ,00H ,00H ,00H ,00H ,00H
db 00H ,00H ,00H ,00H ,020H ,070H ,0a8H ,0fcH ,0a8H ,020H ,070H ,00H ,00H ,00H ,00H ,00H
db 00H ,00H ,00H ,00H ,020H ,070H ,0f8H ,0fcH ,0acH ,020H ,070H ,00H ,00H ,00H ,00H ,00H
db 00H ,00H ,00H ,00H ,00H ,00H ,030H ,078H ,078H ,030H ,00H ,00H ,00H ,00H ,00H ,00H
makeFont程序的邏輯簡(jiǎn)單,只是把一行一行的文件讀入届氢,把一行中的"."當(dāng)做0欠窒,"*"當(dāng)做1,然后轉(zhuǎn)換成16進(jìn)制數(shù)寫入文件fontData.inc即可退子。
有了上面的字體二進(jìn)制文件后岖妄,我們直接將它include到內(nèi)核文件kernel.asm里,然后在我們的C語言程序中直接使用即可寂祥,在C語言中荐虐,我們先聲明一個(gè)外部變量數(shù)組:
extern char systemFont[16]
要想繪制某個(gè)字符,例如字符B,我們可以使用以下調(diào)用
showFont8(vram, xsize, 20, 20, COL8_FFFFFF, systemFont+'A'*16);
接下來我們給出顯示字符A,B,C,1,2,3的C語言部分代碼:
extern char systemFont[16];
void showFont8(char *vram, int xsize, int x, int y, char c, char* font);
void CMain(void) {
struct BOOTINFO bootInfo;
initBootInfo(&bootInfo);
char*vram = bootInfo.vgaRam;
int xsize = bootInfo.screenX, ysize = bootInfo.screenY;
init_palette();
boxfill8(vram, xsize, COL8_008484, 0, 0, xsize-1, ysize-29);
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize-28, xsize-1, ysize-28);
boxfill8(vram, xsize, COL8_FFFFFF, 0, ysize-27, xsize-1, ysize-27);
boxfill8(vram, xsize, COL8_C6C6C6, 0, ysize-26, xsize-1, ysize-1);
boxfill8(vram, xsize, COL8_FFFFFF, 3, ysize-24, 59, ysize-24);
boxfill8(vram, xsize, COL8_FFFFFF, 2, ysize-24, 2, ysize-4);
boxfill8(vram, xsize, COL8_848484, 3, ysize-4, 59, ysize-4);
boxfill8(vram, xsize, COL8_848484, 59, ysize-23, 59, ysize-5);
boxfill8(vram, xsize, COL8_000000, 2, ysize-3, 59, ysize-3);
boxfill8(vram, xsize, COL8_000000, 60, ysize-24, 60, ysize-3);
boxfill8(vram, xsize, COL8_848484, xsize-47, ysize-24, xsize-4, ysize-24);
boxfill8(vram, xsize, COL8_848484, xsize-47, ysize-23, xsize-47, ysize-4);
boxfill8(vram, xsize, COL8_FFFFFF, xsize-47, ysize-3, xsize-4, ysize-3);
boxfill8(vram, xsize, COL8_FFFFFF, xsize-3, ysize-24, xsize-3, ysize-3);
showFont8(vram, xsize, 8, 8, COL8_FFFFFF, systemFont + 'A'*16);
showFont8(vram, xsize, 16, 8, COL8_FFFFFF, systemFont + 'B'*16);
showFont8(vram, xsize, 24, 8, COL8_FFFFFF, systemFont + 'C'*16);
showFont8(vram, xsize, 32, 8, COL8_FFFFFF, systemFont + '1'*16);
showFont8(vram, xsize, 48, 8, COL8_FFFFFF, systemFont + '2'*16);
showFont8(vram, xsize, 64, 8, COL8_FFFFFF, systemFont + '3'*16);
for(;;) {
io_hlt();
}
}
將代碼編譯并結(jié)合如內(nèi)核后丸凭,運(yùn)行效果如下:
字體C的顯示好像有點(diǎn)問題福扬,后面我會(huì)查查代碼腕铸,并及時(shí)更新。
####****顯示字符串
我們能夠顯示單個(gè)字符铛碑,只要稍加加工狠裹,我們就可以顯示一個(gè)字符串,顯示字符串只不過是將字符連在一起顯示罷了汽烦,具體代碼如下:
void showString(char* vram, int xsize, int x, int y, char color, unsigned char *s ) {
for (; *s != 0x00; s++) {
showFont8(vram, xsize, x, y,color, systemFont+ *s * 16);
x += 8;
}
}
編譯結(jié)合進(jìn)內(nèi)核后涛菠,運(yùn)行效果如下:
本文的所有代碼均可在網(wǎng)易云課堂下載:
Linux kernel Hacker, 從零構(gòu)建自己的內(nèi)核