UEFI开发学习13 – 中文显示(Ⅲ)

副标题:给UEFI移植一个字体引擎 - FreeType2

前面两篇介绍中文显示的文章都有一个共同的做法,那就是要生成字库。今天要写的就是无需生成字库,直接提供一个字体文件即可显示中文,或者其它任意字符。

Free Type2介绍

FreeType 2是一种软件字体引擎,旨在小型,高效,高度可定制和便携式,同时能够产生高质量的输出(字形图像)。它也可以用于图形库,显示服务器,字体转换工具,文本图像生成工具以及许多其他产品。

FreeType 2是一种字体服务,不提供执行诸如文本布局或图形处理(例如,彩色文本呈现等)之类的高级功能的API。但是,它通过提供一个简单,易于使用且统一的界面来访问字体文件的内容。前面所写的文章字库都需要工具生成的,现在通过Freetype,便可以省了这个过程,可以直接调用FreeType得到字体的点阵。

官网:https://www.freetype.org/

下载:https://download.savannah.gnu.org/releases/freetype/

Free Type2移植

1.  在AppPkg\Applications新建文件夹Freetype2,下载freetype最新的压缩包(目前是freetype-2.10.4)解压出来,将include,src目录复制到此目录

2. 新建Freetype.inf,文本方式打开freetype-2.10.4\builds\windows\vc2010\freetype.vcxproj.filters,将所有.c文件路径复制到inf,最后再加上ft2build.h及其路径

3. Freetype.inf添加

[BuildOptions]
  MSFT:*_*_*_CC_FLAGS = /D FT2_BUILD_LIBRARY /wd4244 /wd4706
  GCC:*_*_*_CC_FLAGS = -DFT2_BUILD_LIBRARY -w

4. 添加Freetype.c并加一个空main函数

5. 编译,可能有错误,但应该很容易解决的

Free Type2的使用

首先看一个不使用UEFI相关库的最简单的例子,该例子从字体文件中获取指定大小的Bitmap,然后使用字符的方式显示出来:

#include <stdio.h>
#include <string.h>
#include <math.h>

#include <ft2build.h>
#include FT_FREETYPE_H

int main(int argc, char **argv)
{
    FT_Library                       library;
    FT_Face                          face;
    FT_Error                         error;
    FT_UInt                          char_index;
    wchar_t                          ch = L'永';

    /* Init FT2 */ 
    error = FT_Init_FreeType(&library);
 
    /* Load font */
    error = FT_New_Face(library, "./SourceHanSans-Normal.ttc", 0, &face);

    /* Set font size */
    error = FT_Set_Char_Size(face, 60 * 64, 40 * 64, 60, 40);

    /* Get graphy font char index */
    char_index = FT_Get_Char_Index(face, ch);

    /* Load char graphy */
    FT_Load_Glyph(face, char_index, FT_LOAD_COLOR);

    /* Get bitmap */
    if (face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
    {
        FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
    }
    
    FT_UInt    i, j;
    FT_UInt    width, height;

    width  = face->glyph->bitmap.width;
    height = face->glyph->bitmap.rows;

    for (i = 0; i < height; i ++) {
        for (j = 0; j < width; j ++) {
            putchar( face->glyph->bitmap.buffer[i * width + j] == 0 ? ' ': face->glyph->bitmap.buffer[i * width + j] < 128 ? '+': '*' );
        }
        putchar( '\n' );
    }

    printf("width:%d, height:%d\n", width, height);

    return 0;
}

注意,程序中有中文字符,文件保存的时候编码要选择UTF-8 with BOM,否则得到的Bitmap是错误的!运行结果:

有了这个bitmap,我们就可以把它转换成BltBuffer,然后再用Blt函数绘制出来。既然是UEFI,那在.c中使用中文字符是不大规范的,而且还要保存成特殊的格式,所以可以放到uni文件中。代码如下:

#include <Uefi.h>
#include <Protocol/GraphicsOutput.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/UefiBootServicesTableLib.h>
#include <Library/HiiLib.h>
#include <Library/UefiHiiServicesLib.h>

#include <stdio.h>
#include <string.h>
#include <math.h>

#include <ft2build.h>
#include FT_FREETYPE_H

EFI_GUID    mPackageListGuid = {0xd8ebc548, 0xd7ea, 0x46e6, {0x98, 0x0d, 0x0e, 0xa0, 0x04, 0xfd, 0xc7, 0x00}};

EFI_STATUS 
AllocateBltBuffer (
    IN   EFI_GRAPHICS_OUTPUT_PROTOCOL     *  GraphicsOutput,
    IN   UINTN                               SourceX,
    IN   UINTN                               SourceY,
    IN   UINTN                               Width,
    IN   UINTN                               Height,
    IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL **  BltBuffer
)
{
    EFI_STATUS Status;

    *BltBuffer = AllocateZeroPool(Width * Height * sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL));

    Status =  GraphicsOutput->Blt(
                GraphicsOutput,
                *BltBuffer,
                EfiBltVideoToBltBuffer,
                SourceX, SourceY,
                0, 0,
                Width,
                Height,
                0
    );

    return Status;
}

VOID 
BitmapToUefiBltBuffer (
    IN UINT8 * Bitmap,
    IN UINTN   Width,
    IN UINTN   Height,
    IN OUT EFI_GRAPHICS_OUTPUT_BLT_PIXEL * BltBuffer
)
{
    UINTN Rows;
    UINTN Columns;
    UINTN Color;

    for (Rows = 0; Rows < Height; Rows ++) {
        for (Columns = 0; Columns < Width; Columns ++) {
            if (Bitmap[Rows * Width + Columns]) {
                
                /* Fill with white */
                Color = 0xFF;
                BltBuffer[Rows * Width + Columns].Blue  = Color;
                BltBuffer[Rows * Width + Columns].Green = Color;
                BltBuffer[Rows * Width + Columns].Red   = Color; 
            }
        }
    }
}

EFI_STATUS
DrawChar (
    IN   EFI_GRAPHICS_OUTPUT_PROTOCOL  * GraphicsOutput,
    IN   UINTN                           DestinationX,
    IN   UINTN                           DestinationY,
    IN   UINTN                           Width,
    IN   UINTN                           Height,
    IN   EFI_GRAPHICS_OUTPUT_BLT_PIXEL * BltBuffer
)
{
    EFI_STATUS Status;

    Status =  GraphicsOutput->Blt(
                GraphicsOutput,
                BltBuffer,
                EfiBltBufferToVideo,
                0, 0,
                DestinationX, DestinationY,
                Width,
                Height,
                0
    );

    return Status;
}


int main(int argc, char **argv)
{
    FT_Library	                     library;
    FT_Face		                     face;
    FT_Error	                     error;
    FT_UInt		                     charIdx;

    EFI_STATUS                       Status;
    EFI_GRAPHICS_OUTPUT_PROTOCOL  *  GraphicsOutput;
    EFI_GRAPHICS_OUTPUT_BLT_PIXEL *  BltBuffer;
    UINTN                            Width, Height;
    UINTN                            X, Y;
    UINTN                            Index;

    EFI_HANDLE     HiiHandle;
    EFI_STRING     String;
    //CHAR8        * Language = "en-US";
    CHAR8        * Language = "zh-Hans";

    HiiHandle = HiiAddPackages (&mPackageListGuid, gImageHandle, FreetypeStrings, NULL);  
    String    = HiiGetString (HiiHandle, STRING_TOKEN (STR_STRING), Language);

    Status = gBS->LocateProtocol(&gEfiGraphicsOutputProtocolGuid, NULL, (VOID **) &GraphicsOutput);
    if (EFI_ERROR(Status)) {
        return 0;
    }

    /* Init FT2 */ 
    error = FT_Init_FreeType(&library);
 
    /* Load font */
    error = FT_New_Face(library, "./SourceHanSans-Normal.ttc", 0, &face);

    X = 200;
    Y = 200;


    for (Index = 0; String[Index] != 0; Index ++) {
        /* Set font size */
        error = FT_Set_Char_Size(face, (40 + Index * 15) * 64, (40 + Index * 15) * 64, 80, 80);

        /* Get graphy font char index */
        charIdx = FT_Get_Char_Index(face, String[Index]);
    
        /* Load char graphy */
        FT_Load_Glyph(face, charIdx, FT_LOAD_COLOR);

        /* Get bitmap */
        if (face->glyph->format != FT_GLYPH_FORMAT_BITMAP)
        {
            FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
        }

        Width  = face->glyph->bitmap.width;
        Height = face->glyph->bitmap.rows;

        AllocateBltBuffer(GraphicsOutput, X, Y, Width, Height, &BltBuffer);
        BitmapToUefiBltBuffer(face->glyph->bitmap.buffer, Width, Height, BltBuffer);
        DrawChar(GraphicsOutput, X, Y, Width, Height, BltBuffer);

        X += (Width + 10);
    }

    return 0;
}

uni文件

#langdef   en-US "English"
#langdef   zh-Hans "中文"

#string STR_STRING           #language en-US    "They come the security"
                             #language zh-Hans  "既来之 则安之"

结果如下:

源码

Github:https://github.com/ay123net/uefift2

Gitee:https://gitee.com/ay123net/uefift2

 

 

版权声明:
作者:bin
链接:https://ay123.net/mystudy/995/
来源:爱影博客
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
海报
UEFI开发学习13 – 中文显示(Ⅲ)
副标题:给UEFI移植一个字体引擎 - FreeType2 前面两篇介绍中文显示的文章都有一个共同的做法,那就是要生成字库。今天要写的就是无需生成字库,直接提供一个……
<<上一篇
下一篇>>