精品伊人久久大香线蕉,开心久久婷婷综合中文字幕,杏田冲梨,人妻无码aⅴ不卡中文字幕

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
【BIOS】讓UEFI BIOS支持漢字顯示:漢字編碼與顯示實(shí)踐

我們通常看到的UEFI BIOS都是大片英文字符,對(duì)于中國用戶而言,提供中文提示可以幫助我們建立更好的用戶體驗(yàn)。那么如何在BIOS中顯示中文呢,其實(shí)比較簡(jiǎn)單,我們分成兩個(gè)部分介紹,第一部分介紹字符編碼理論,第二部分介紹UEFI實(shí)踐。如果你對(duì)字符編碼理論很了解,可以直接從第二部分看起。

UEFI在訂立之初就充分考慮了多國語言的管理、編碼和顯示問題。多語言的管理不在本文的范圍內(nèi),大家可以參考UEFI spec。UEFI中的字符串統(tǒng)一采用Unicode的UCS-2編碼。說起Unicode,它很容易和ASCII、UTF-8、UTF-16、UTF-32和GB2312編碼等等概念混淆混淆。我們先來從歷史沿革來看看他們都是什么,各自的優(yōu)缺點(diǎn)都是什么。

Unicode、UTF-16與UCS-2

如果你是一個(gè)生活在2003年的程序員,卻不了解字符、字符集、編碼和Unicode這些基礎(chǔ)知識(shí)。那你可要小心了,要是被我抓到你,我會(huì)讓你在潛水艇里剝六個(gè)月洋蔥來懲罰你。

這個(gè)邪惡的恫嚇是Joel Spolsky在1993首次發(fā)出的,時(shí)至今日,unicode已經(jīng)廣泛使用在了我們周圍,2003年到了現(xiàn)在又過去十幾年了,如果還不知道unicode,被他抓住就不只是剝洋蔥這么簡(jiǎn)單了。。。

為了避免被他抓住的可悲下場(chǎng)。我們趕緊來復(fù)習(xí)下字符編碼的歷史和unicode的產(chǎn)生。

1

歷史

話說很久以前,計(jì)算機(jī)制造商有自己的表示字符的方式。他們并不需要擔(dān)心如何和其它計(jì)算機(jī)交流,并提出了各自的方式來將字形渲染到屏幕上。隨著計(jì)算機(jī)越來越流行,廠商之間的競(jìng)爭(zhēng)更加激烈,在不同的計(jì)算機(jī)體系間轉(zhuǎn)換數(shù)據(jù)變得十分頭疼,人們厭煩了這種自定義造成的混亂。

最終,計(jì)算機(jī)制造商一起制定了一個(gè)標(biāo)準(zhǔn)的方法來描述字符。他們定義使用一個(gè)字節(jié)的低7位來表示字符,例如,字母A是65,c是99,~是126等等, ASCII碼就這樣誕生了。原始的ASCII標(biāo)準(zhǔn)定義了從0到127 的字符,這樣正好能用七個(gè)比特表示。不過好景不長,它國家的人趁這個(gè)機(jī)會(huì)開始使用128到255范圍內(nèi)的編碼來表達(dá)自己語言中的字符。例如,144在阿拉伯人的ASCII碼中是?,而在俄羅斯的ASCII碼中是?。即使在美國,對(duì)于未使用區(qū)域也有各種各樣的利用。IBM PC就出現(xiàn)了“OEM 字體”或”擴(kuò)展ASCII碼”,為用戶提供漂亮的圖形文字來繪制文本框并支持一些歐洲字符,例如英鎊(£)符號(hào)。

勤勞的中國人民也行動(dòng)起來,我們不客氣地把那些127號(hào)之后的奇異符號(hào)們直接取消掉, 規(guī)定:一個(gè)小于127的字符的意義與原來相同,但兩個(gè)大于127的字符連在一起時(shí),就表示一個(gè)漢字,前面的一個(gè)字節(jié)(他稱之為高字節(jié))從0xA1用到0xF7,后面一個(gè)字節(jié)(低字節(jié))從0xA1到0xFE,這樣我們就可以組合出大約7000多個(gè)簡(jiǎn)體漢字了。在這些編碼里,我們還把數(shù)學(xué)符號(hào)、羅馬希臘的字母、日文的假名們都編進(jìn)去了,連在 ASCII 里本來就有的數(shù)字、標(biāo)點(diǎn)、字母都統(tǒng)統(tǒng)重新編了兩個(gè)字節(jié)長的編碼,這就是常說的'全角'字符,而原來在127號(hào)以下的那些就叫'半角'字符了。

中國人民看到這樣很不錯(cuò),于是就把這種漢字方案叫做 'GB2312'。GB2312 是對(duì) ASCII 的中文擴(kuò)展。但是中國的漢字太多了,我們很快就就發(fā)現(xiàn)有許多人的人名沒有辦法在這里打出來,于是我們不得不繼續(xù)把 GB2312 沒有用到的碼位找出來老實(shí)不客氣地用上。

后來還是不夠用,于是干脆不再要求低字節(jié)一定是127號(hào)之后的內(nèi)碼,只要第一個(gè)字節(jié)是大于127就固定表示這是一個(gè)漢字的開始,不管后面跟的是不是擴(kuò)展字符集里的內(nèi)容。結(jié)果擴(kuò)展之后的編碼方案被稱為 GBK 標(biāo)準(zhǔn),GBK 包括了 GB2312 的所有內(nèi)容,同時(shí)又增加了近20000個(gè)新的漢字(包括繁體字)和符號(hào)。

后來少數(shù)民族也要用電腦了,于是我們?cè)贁U(kuò)展,又加了幾千個(gè)新的少數(shù)民族的字,GBK 擴(kuò)成了 GB18030。從此之后,中華民族的文化就可以在計(jì)算機(jī)時(shí)代中傳承了。

這帶來了一點(diǎn)小麻煩:字符串長度和編碼的長度不唯一對(duì)應(yīng),以為常常處理中英夾雜的問題。這也難不倒中國的程序猿們,一個(gè)有(di)趣(xiao)的函數(shù)就可以解決。另一個(gè)稍大點(diǎn)的麻煩是錯(cuò)誤的傳染性,在某處一個(gè)字節(jié)的缺失會(huì)使隨后的其他文章成為亂碼!

當(dāng)各個(gè)國家都像中國這樣搞出一套自己的編碼標(biāo)準(zhǔn),結(jié)果互相之間誰也不懂誰的編碼,誰也不支持別人的編碼,連大陸和臺(tái)灣這樣只相隔了150海里,使用著同一種語言的兄弟地區(qū),也搓弄出來自己的編碼:BIG5。天哪,從此黑暗籠罩在編碼領(lǐng)域,工程師和文檔管理大師們都念下面這個(gè)咒語:“換個(gè)編碼,這些亂碼一定就會(huì)好的,芝麻開門吧!”

ISO國際組織實(shí)在不能不管了,于是他們?cè)?991年重新搞一個(gè)包括了地球上所有文化、所有字母和符號(hào)的編碼!他們打算叫它'Universal Multiple-Octet Coded Character Set',簡(jiǎn)稱 UCS, 俗稱 'UNICODE'。他們雄心勃勃,創(chuàng)立了一個(gè)65535個(gè)碼表的字符集,認(rèn)為能Cover所有的字符,這個(gè)集合就是UCS-2。這里我只能說他們Too young, too simple, sometimes…咳咳。我天朝文字界表示不服,康熙字典就有4萬多,再看看我們的詞源和辭海,嚇?biāo)滥銈儯№n國人也表示不服:“漢字是我們韓國人發(fā)明的!”,更別提還有別的國家的各種蝌蚪文。幸虧ISO知錯(cuò)能改后面推出UCS-4方案,說簡(jiǎn)單了就是四個(gè)字節(jié)來表示一個(gè)字符,這樣我們就可以組合出21億個(gè)不同的字符出來(最高位有其他用途),這大概將來外星人入侵時(shí)可以將外星人的文字也包括進(jìn)來!

Unicode背后的想法非常簡(jiǎn)單,然而卻被普遍的誤解了。Unicode就像一個(gè)電話本,標(biāo)記著字符和數(shù)字之間的映射關(guān)系。Joel稱之為「神奇數(shù)字」,因?yàn)樗鼈兛赡苁请S機(jī)指定的,而且不會(huì)給出任何解釋。官方術(shù)語是碼位(Code Point),總是用U+開頭。理論上每種語言中的每種字符都被Unicode協(xié)會(huì)指定了一個(gè)神奇數(shù)字。例如希伯來文中的第一個(gè)字母?,是U+2135,字母A是U+0061。Unicode并不涉及字符是怎么在字節(jié)中表示的,它僅僅指定了字符對(duì)應(yīng)的數(shù)字,僅此而已。他的編碼和傳輸?shù)膯栴}是UTF(Unicode Transformation Formats)定義的,我們通常會(huì)遇到的是UTF-8,UTF-16和UTF-32。

低字節(jié)序(Little Endian)和高字節(jié)序(Big Endian)


Endian讀作End-ian或者Indian。這個(gè)術(shù)語的起源可以追溯到格列佛游記。(小說中,小人國為水煮蛋應(yīng)該從大的一端(Big-End)剝開還是小的一端(Little-End)剝開而爭(zhēng)論,爭(zhēng)論的雙方分別被稱為“大端派”和“小端派”。)字節(jié)序方案只是一個(gè)微處理器架構(gòu)設(shè)計(jì)者的偏好問題,例如,Intel使用低字節(jié)序,Motorola使用高字節(jié)序。UEFI中當(dāng)然也是使用Little-endian.

2

UTF-8

UTF-8是一個(gè)非常驚艷的概念,它漂亮的實(shí)現(xiàn)了對(duì)ASCII碼的向后兼容,以保證Unicode可以被大眾接受。發(fā)明它的人至少應(yīng)該得個(gè)諾貝爾和平獎(jiǎng)。

在UTF-8中,0-127號(hào)的字符用1個(gè)字節(jié)來表示,使用和US-ASCII相同的編碼。這意味著1980年代寫的文檔用UTF-8打開一點(diǎn)問題都沒有。只有128號(hào)及以上的字符才用2個(gè),3個(gè)或者4個(gè)字節(jié)來表示。因此,UTF-8被稱作可變長度編碼。

3

UTF-16

另一個(gè)流行的可變長度編碼方案是UTF-16,它使用2個(gè)或者4個(gè)字節(jié)來存儲(chǔ)字符。如果字符編碼小于0x10000,則UTF-16直接用UCS-2的兩個(gè)字節(jié)表示,否則用變換后的四字節(jié)表示。變換通過一個(gè)特殊保留的區(qū)間用來表示大于FFFF的值。這些值會(huì)通過4個(gè)字節(jié)來編碼。這里不再詳述。

4

UTF-32

定長編碼,所有字符都是四個(gè)字節(jié)。即UCS-4直接搬過來。

5

各種編碼方式優(yōu)缺點(diǎn)以及UEFI的選擇

英語國家通常都和字母打交道,使用UTF-32會(huì)是一種巨大的浪費(fèi),想象一下你的.c程序忽然擴(kuò)大四倍的樣子!用UTF-16也會(huì)有些浪費(fèi)。似乎UTF-8很優(yōu)秀了?其實(shí)還是那個(gè)問題,字符串長度和編碼長度不一致,導(dǎo)致要做些運(yùn)算才能得到字符串長度。而UTF-32就方便了,直接字符串長度除以4即可!

UEFI采取了折中的方式,沒有采用他們中任何一個(gè),而是采用標(biāo)準(zhǔn)的UCS-2編碼,即每個(gè)字符占兩個(gè)字節(jié),字符串長度=編碼長度 / 2;(強(qiáng)迫癥發(fā)作,不得不加個(gè)分號(hào)) ,對(duì)于UTF-16的擴(kuò)展部分,答案很簡(jiǎn)單,不支持

字符顯示原理

了解完了字符編碼,我們接著看看一個(gè)字符如何顯示出來。UEFI提供GOP或UGA protocol,我們可以用它來顯示一幅位圖。同樣,如果我們有了點(diǎn)陣字庫,我們也可以用它來顯示字符。幸運(yùn)的是,UEFI已經(jīng)替我們考慮好了怎么顯示字符,它定義了SimpleFont格式。SimpleFont是一種點(diǎn)陣字體,有兩種格式,一種是窄體字,一種是寬體字。窄體字是一種8×19的點(diǎn)陣字庫,寬體字是16×19的點(diǎn)陣字庫,分成兩半,前一半表示左邊部分,后一半表示右邊部分。點(diǎn)陣字庫中每一位(bit)代表一個(gè)像素。我們以英文E做例子:

它的編碼是:

EFI_NARROW_GLYPH GlyphData[]  = {…

     {0x0045, 0x00,{0x00, 0x00, 0x00, 0xFE, 0x66, 0x62,0x60, 0x68, 0x78, 0x68,0x60, 0x60,    0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00}},

     …};

我們可以通過HiiAddPackages的形式注冊(cè)字體點(diǎn)陣到HII的數(shù)據(jù)倉庫中。在EDKII的GraphicConsoleDxe驅(qū)動(dòng)中有現(xiàn)成的例子。窄體字點(diǎn)陣在MdeModulePkg\Universal\Console\GraphicsConsoleDxe\ LaffStd.c

中,注冊(cè)函數(shù)在同一個(gè)驅(qū)動(dòng)的GraphicsConsole.c的RegisterFontPackage函數(shù)里:

PackageLength   = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mNarrowFontSize + 4;

  Package = AllocateZeroPool (PackageLength);

  ASSERT (Package != NULL);

 

  WriteUnaligned32((UINT32 *) Package,PackageLength);

  SimplifiedFont = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *) (Package + 4);

  SimplifiedFont->Header.Length        = (UINT32) (PackageLength - 4);

  SimplifiedFont->Header.Type          = EFI_HII_PACKAGE_SIMPLE_FONTS;

  SimplifiedFont->NumberOfNarrowGlyphs = (UINT16) (mNarrowFontSize / sizeof (EFI_NARROW_GLYPH));

 

  Location = (UINT8 *) (&SimplifiedFont->NumberOfWideGlyphs + 1);

  CopyMem (Location, gUsStdNarrowGlyphData, mNarrowFontSize);


  mHiiHandle = HiiAddPackages (

                 &mFontPackageListGuid,

                 NULL,

                 Package,

                 NULL

                 );

注意Simple Font同時(shí)支持寬體和窄體兩種。那么漢字應(yīng)該是什么樣呢,我們以“永”字來舉個(gè)例子:

按照寬體字的編碼標(biāo)準(zhǔn)應(yīng)該編做:

EFI_WIDE_GLYPH gUsStdWideGlyphData[] = {

  { 0x6c38, 0x00, {0x00,0x02,0x01,0x00,0x1F,0x01,0x01,0x7D,0x05,0x05,0x09,0x09,0x11,0x21,0x41,0x05,0x02,0x00,0x00},                  {0x00,0x00,0x00,0x00,0x00,0x08,0x18,0xA0,0xC0,0x40,0x20,0x20,0x10,0x0E,0x04,0x00,0x00,0x00,0x00},  

 {0x00,0x00,0x00}},

     …};

很簡(jiǎn)單是不是?

實(shí)踐

1

環(huán)境搭建

有了前面的理論我們來驗(yàn)證一下, NT32提供了很好的實(shí)驗(yàn)環(huán)境。我們先從Github上下載最新的EDKII的源程序。接下來配置編譯環(huán)境:

Edksetup –nt32

環(huán)境設(shè)置好后我們編譯一下:

Build

一切順利的話,我們可以運(yùn)行看看:

Build run

2

編程

到這里,實(shí)驗(yàn)環(huán)境搭建完成。我們開始進(jìn)行我們最喜歡的活動(dòng)吧!打開LaffStd.c,加入“永”的點(diǎn)陣字模:

EFI_WIDE_GLYPH gUsStdWideGlyphData[] = {

  { 0x6c38, 0x00, {0x00,0x02,0x01,0x00,0x1F,0x01,0x01,0x7D,0x05,0x05,0x09,0x09,0x11,0x21,0x41,0x05,0x02,0x00,0x00},              {0x00,0x00,0x00,0x00,0x00,0x08,0x18,0xA0,0xC0,0x40,0x20,0x20,0x10,0x0E,0x04,0x00,0x00,0x00,0x00},  {0x00,0x00,0x00}},

  { 0x0000, 0x00, {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},                  {0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},  {0x00,0x00,0x00}}            //EOL

};

UINT32 mWideFontSize =  sizeof (gUsStdWideGlyphData);

接下來在GraphicConsoleDxe.c里將其和窄體字一起注冊(cè)到HII(紅色是新加的):

PackageLength   = sizeof (EFI_HII_SIMPLE_FONT_PACKAGE_HDR) + mNarrowFontSize + mWideFontSize + 4;

  Package = AllocateZeroPool (PackageLength);

  ASSERT (Package != NULL);

  WriteUnaligned32((UINT32 *) Package,PackageLength);

  SimplifiedFont = (EFI_HII_SIMPLE_FONT_PACKAGE_HDR *) (Package + 4);

  SimplifiedFont->Header.Length        = (UINT32) (PackageLength - 4);

  SimplifiedFont->Header.Type          = EFI_HII_PACKAGE_SIMPLE_FONTS;

  SimplifiedFont->NumberOfNarrowGlyphs = (UINT16) (mNarrowFontSize / sizeof (EFI_NARROW_GLYPH));

 

  Location = (UINT8 *) (&SimplifiedFont->NumberOfWideGlyphs + 1);

  CopyMem (Location, gUsStdNarrowGlyphData, mNarrowFontSize);

  SimplifiedFont->NumberOfWideGlyphs = (UINT16) (mWideFontSize / sizeof (EFI_WIDE_GLYPH));

  Location += mNarrowFontSize;

  CopyMem (Location, gUsStdWideGlyphData, mWideFontSize);

大功告成,開香檳!Wait a minute,好像需要驗(yàn)證一下。我們從MdeModulePackage的HelloWorld Shell應(yīng)用下手,加入下面兩行到main函數(shù):

  Print (L'I like it!\n');

  Print (L'永\n');

試試看吧。運(yùn)行NT32環(huán)境,進(jìn)入shell,鍵入HelloWorld,得到如下:

一切正常!順便考驗(yàn)一下HII對(duì)寬體和窄體字混排會(huì)不會(huì)出問題,代碼改成如下:

  Print (L'I like it!\n');

  Print (L'永\n');

  Print (L'I永like it!\n');

  Print (L'Il永ike it!\n');

再次編譯測(cè)試,結(jié)果如下:

噠噠,HII順利過關(guān)!

后記

這里只舉了一個(gè)字的字模,其他的字怎么辦?有一種辦法是在Windows上用程序抓取True Type的字庫,將其轉(zhuǎn)換成點(diǎn)陣字庫。即寫個(gè)簡(jiǎn)單的python程序,將true type的字投影到16×19的方格上,投影的每個(gè)格子有色即是1,無色即是0。簡(jiǎn)單方便,瞬間就可以完成。這里有兩個(gè)小問題:

1.      True Type字庫是矢量字庫,放大不會(huì)變形。放到我們的小格子里面,有些邊邊角角需要在修飾一下。可以借助網(wǎng)上免費(fèi)的小工具,數(shù)量也不會(huì)太多。

2.      字體是有版權(quán)的!不能亂用。我們可以找些Open的字庫,將其copy到windows的font目錄下即可。

最后要提醒的是,如果用在產(chǎn)品中最好另外寫個(gè)模塊單獨(dú)注冊(cè),不要全部加到GraphicConsoleDxe中去了。


本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
unicode編碼
EF BB BF Unicode簽名BOM(Byte Order Mark)
刨根究底字符編碼之七——ANSI編碼與代碼頁(Code Page)
JS計(jì)算字符串所占字節(jié)數(shù)
ASCII、ANSI、UNICODE及UTF-8編碼
ASCII, GB2312, Unicode , UTF-8 - 靈感點(diǎn)滴
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服

主站蜘蛛池模板: 万载县| 嘉禾县| 桑日县| 清河县| 永城市| 武鸣县| 丹江口市| 新兴县| 台北县| 金阳县| 正镶白旗| 财经| 东阳市| 石棉县| 托里县| 南康市| 莎车县| 惠州市| 蚌埠市| 行唐县| 华亭县| 张家港市| 武宁县| 石首市| 乐陵市| 大厂| 邻水| 扎赉特旗| 油尖旺区| 施秉县| 剑川县| 包头市| 山东| 临沂市| 肥城市| 琼中| 青浦区| 盐山县| 平陆县| 昆明市| 广河县|