首页 > 解决方案 > 3.5" TFT LCD 上位图或光栅化字体位图和文本显示的差异

问题描述

我使用的是 3.5:TFT LCD 显示器,带有 Arduino Uno 和制造商提供的库 KeDei TFT 库。该库带有一个位图字体表,对于 Arduino Uno 的少量内存来说它是巨大的,所以我一直在寻找替代品。

我遇到的是似乎没有标准的表示形式,我发现一些位图字体表工作正常,而另一些则显示为奇怪的涂鸦和标记,或者它们显示颠倒,或者它们显示时字母翻转。在编写了一个简单的应用程序来显示一些字符后,我终于意识到不同的位图使用不同的字符方向。

我的问题

位图字体的位数据的规则或标准或预期表示是什么?为什么位图字体似乎有几种不同的文本字符方向?

关于问题的想法

这些是否是由于不同的目标设备(例如 Windows 显示驱动程序或 Linux 显示驱动程序)与裸机 Arduino TFT LCD 显示驱动程序?

用于将特定位图字体表示确定为一系列无符号字符值的标准是什么?通过设置像素颜色在显示表面上绘图时,不同类型的光栅设备(例如 TFT LCD 显示器及其控制器)是否具有不同的位序列?

还有哪些其他可能的位图字体表示需要我的库版本当前不提供的转换?

除了我用来确定需要什么转换的方法之外,还有其他方法吗?我目前将位图字体表插入测试程序并打印出一组字符以查看其外观,然后通过使用 Arduino 和 TFT LCD 屏幕进行测试来微调转换。

到目前为止我的经验

KeDei TFT 库带有一个位图字体表,定义为

const unsigned char font_table_16_col[96][16] = {
    { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },/*" ",0*/
    { 0x00,0x00,0x00,0x18,0x18,0x18,0x18,0x18,0x18,0x18,0x00,0x00,0x38,0x38,0x00,0x00 },/*"!",1*/
    { 0x00,0xD8,0xFC,0x6C,0x36,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },/*""",2*/
    { 0x00,0x00,0x00,0x6C,0x6C,0x6C,0xFF,0x36,0x36,0x36,0xFF,0x36,0x36,0x36,0x00,0x00 },/*"#",3*/
    { 0x00,0x00,0x18,0x3C,0x7E,0x7E,0x1E,0x1C,0x38,0x78,0x78,0x7E,0x7E,0x3C,0x18,0x18 },/*"$",4*/
    { 0x00,0x00,0x00,0x66,0x6F,0x3F,0x3F,0x3F,0x7E,0xF8,0xFC,0xFC,0xFC,0x66,0x00,0x00 },/*"%",5*/
...

我并不完全熟悉位图字体的标​​准描述,但我认为这是一种 8x16 位图字体,其中每个字符的宽度为 8 像素,高度为 16 像素,或者是 8x16 位图字体。

由于这张表的大小和 Arduino Uno 上的少量内存,我开始寻找其他清晰易读的位图字体,同时占用更少的内存。请参阅减少用于 Arduino 的 3.5" TFT 显示器的 KeDei TFT 库所需的内存

我希望找到的是围绕 6x6 位图字体的东西,这样位图字体表的定义就会改变,const unsigned char font_table_16_col[96][16] = {从而const unsigned char font_table_16_col[96][6] = {释放大量内存。通过删除小写字母来减少表格的实验表明,这也有帮助。

寻找替代的位图字体比我想象的要困难,设想某人在 GitHub 存储库中的某个地方拥有位图字体的母体,通过一两次搜索很容易找到。

我遇到的是,虽然我发现了几个不同的位图字体示例,但似乎并非都与我特定的 3.5" TFT LCD 显示器兼容。

例如,这里是四种不同位图字体的表示,显示了两个字符的位图位,感叹号 (!) 和双引号 (")。5x8 似乎顺时针旋转了 90 度。8x8 和16x8 似乎方向正确,而 13x8 似乎颠倒了。

显示差异的位图字体样本的字符表示

生成上述位图表示

上图中的位图字体表示,显示了文本字符方向的差异,是由一个简单的 Windows GUI 生成的,并用一个短划线 (-) 表示位值 0 和一个星号 (*) 表示位值 1 . 这是 Microsoft Windows GUI 应用程序的输出,WM_PAINT其绘制显示图像的消息处理程序如下:

int paintFontDisplay(HWND hWnd)
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);

    SetTextAlign(hdc, TA_CENTER);

    RECT rect;
    GetClientRect(hWnd, &rect);

    //Logical units are device dependent pixels, so this will create a handle to a logical font that is 48 pixels in height.
    //The width, when set to 0, will cause the font mapper to choose the closest matching value.
    //The font face name will be Impact.
    HFONT hFont = CreateFont(24, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS,
        CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FIXED_PITCH, TEXT("Courier"));
    SelectObject(hdc, hFont);

    // TODO: Add any drawing code that uses hdc here...

    int iFirst = 0;
    int iLast = 10;

    POINT outPoint;
    outPoint.x = rect.left + 80;
    outPoint.y = rect.top + 20;
    for (int i = iFirst; i < iLast; i++) {
        for (int j = 0; j < 5; j++) {
            std::wstring charRep;
            for (unsigned char k = 0x80; k; k >>= 1) {
                if (font_table_5_col[i][j] & k) {
                    charRep += '*';
                }
                else {
                    charRep += '-';
                }
            }
            TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
            outPoint.y += 20;
        }
        outPoint.y += 20 + 20 * 11;
    }

    outPoint.x = outPoint.x + 200;
    outPoint.y = rect.top + 20;
    for (int i = iFirst; i < iLast; i++) {
        for (int j = 0; j < 8; j++) {
            std::wstring charRep;
            for (unsigned char k = 0x80; k; k >>= 1) {
                if (font_table_8_col[i][j] & k) {
                        charRep += '*';
                }
                else {
                    charRep += '-';
                }
            }
            TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
            outPoint.y += 20;
        }
        outPoint.y += 20 + 20 * 8;
    }

    outPoint.x = outPoint.x + 200;
    outPoint.y = rect.top + 20;
    for (int i = iFirst; i < iLast; i++) {
        for (int j = 0; j < 13; j++) {
            std::wstring charRep;
            for (unsigned char k = 0x80; k; k >>= 1) {
                if (font_table_13_col[i][j] & k) {
                    charRep += '*';
                }
                else {
                    charRep += '-';
                }
            }
            TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
            outPoint.y += 20;
        }
        outPoint.y += 20 + 20 * 3;
    }

    outPoint.x = outPoint.x + 200;
    outPoint.y = rect.top + 20;
    for (int i = iFirst; i < iLast; i++) {
        for (int j = 0; j < 16; j++) {
            std::wstring charRep;
            for (unsigned char k = 0x80; k; k >>= 1) {
                if (font_table_16_col[i][j] & k) {
                    charRep += '*';
                }
                else {
                    charRep += '-';
                }
            }
            TextOut(hdc, outPoint.x, outPoint.y, charRep.c_str(), charRep.length());
            outPoint.y += 20;
        }
        outPoint.y += 20;
    }

    EndPaint(hWnd, &ps);

    return 0;
}

位图字体表的前几行如下:

//  following table from URL https://forum.arduino.cc/t/font-generation-for-bitmaps/161582/11
const unsigned char font_table_5_col[96][5] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00 } // 20
    ,{ 0x00, 0x00, 0x5f, 0x00, 0x00 } // 21 !
    ,{ 0x00, 0x07, 0x00, 0x07, 0x00 } // 22 "
    ,{ 0x14, 0x7f, 0x14, 0x7f, 0x14 } // 23 #
    ,{ 0x24, 0x2a, 0x7f, 0x2a, 0x12 } // 24 $

// See https://github.com/dhepper/font8x8
const unsigned char font_table_8_col[96][8] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },   // U+0020 (space)
    { 0x18, 0x3C, 0x3C, 0x18, 0x18, 0x00, 0x18, 0x00 },   // U+0021 (!)
    { 0x36, 0x36, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },   // U+0022 (")
    { 0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00 },   // U+0023 (#)
    { 0x0C, 0x3E, 0x03, 0x1E, 0x30, 0x1F, 0x0C, 0x00 },   // U+0024 ($)

const unsigned char font_table_13_col[96][13] = {
    // from URL https://courses.cs.washington.edu/courses/cse457/98a/tech/OpenGL/font.c
    //  GLubyte rasters[][13] = {
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 },/*" ",0*/
    { 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18 },/*"!",1*/
    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x36, 0x36, 0x36 },/*""",2*/
    { 0x00, 0x00, 0x00, 0x66, 0x66, 0xff, 0x66, 0x66, 0xff, 0x66, 0x66, 0x00, 0x00 },/*"#",3*/
    { 0x00, 0x00, 0x18, 0x7e, 0xff, 0x1b, 0x1f, 0x7e, 0xf8, 0xd8, 0xff, 0x7e, 0x18 },/*"$",4*/

转换字体位图以正确显示

我已经修改了使用位图字体显示文本的代码,以便对于特定的位图,字符绘制逻辑将在位图字体表示为一系列十六进制数字以及如何使用这一系列数字之间执行几种不同类型的转换确定打开哪些像素以及关闭哪些像素。

绘制单行字符的代码如下。该函数的概要是向 LCD 控制器提供一个矩形,指定要修改的显示区域,然后进行一系列两个 8 位写入,以设置该区域中每个像素的两字节 RGB565 颜色值。

static bool TFTLCD::draw_glyph(unsigned short x0, unsigned short y0, TftColor fg_color, TftColor bg_color, unsigned char bitMap, unsigned char bmWidth, unsigned char flags)
{
    // we will fill a single row of 8 pixels by iterating over
    // a bitmap font map of which pixels to set to the foreground
    // color and which pixels to set to the background color for this
    // part of the character to display.

    // first determine whether we are scaling the default width by a multiplier
    // of 2 or 3 times the default size. this allows us to have different sizes
    // of text using the same bitmap font.
    if (flags & 0x01)
        set_area(x0, y0, x0 + bmWidth * 2 - 1, y0); // scale the default width to double wide
    else if (flags & 0x02)
        set_area(x0, y0, x0 + bmWidth * 3 - 1, y0); // scale the default width to tripple wide
    else
        set_area(x0, y0, x0 + bmWidth - 1, y0);    // default width and size with no scaling

    if (flags & 0x20) {    // Font::font_flags & FontTable::Flags_InvertBitOrder
        // inverting the order of painting the bits. means the bitmap of the
        // font would display the text with each character flipped if we did not do this.
        for (unsigned char char_n = 0x80; char_n; char_n >>= 1)
        {
            if (bitMap & char_n)
            {
                w_data(fg_color >> 8);
                w_data(fg_color);
            }
            else {
                w_data(bg_color >> 8);
                w_data(bg_color);
            }
            if (flags & 0x03) {         // double wide or triple wide
                if (bitMap & char_n)
                {
                    w_data(fg_color >> 8);
                    w_data(fg_color);
                }
                else {
                    w_data(bg_color >> 8);
                    w_data(bg_color);
                }
                if (flags & 0x02) {          // triple wide
                    if (bitMap & char_n)
                    {
                        w_data(fg_color >> 8);
                        w_data(fg_color);
                    }
                    else {
                        w_data(bg_color >> 8);
                        w_data(bg_color);
                    }
                }
            }
        }
    }
    else {
        for (unsigned char char_n = 1; char_n; char_n <<= 1)
        {
            if (bitMap & char_n)
            {
                w_data(fg_color >> 8);
                w_data(fg_color);
            }
            else {
                w_data(bg_color >> 8);
                w_data(bg_color);
            }
            if (flags & 0x03) {         // double wide or triple wide
                if (bitMap & char_n)
                {
                    w_data(fg_color >> 8);
                    w_data(fg_color);
                }
                else {
                    w_data(bg_color >> 8);
                    w_data(bg_color);
                }
                if (flags & 0x02) {          // triple wide
                    if (bitMap & char_n)
                    {
                        w_data(fg_color >> 8);
                        w_data(fg_color);
                    }
                    else {
                        w_data(bg_color >> 8);
                        w_data(bg_color);
                    }
                }
            }
        }
    }

    return 1;

使用上述函数绘制完整字符的源代码如下。此代码使用该drawGlyph()函数从上到下绘制一系列文本字符切片。何时完成位图转换取决于位图表示。

unsigned char glyphFlags = ((Font::font_flags & FontTable::Flags_DoubleWide) ? 1 : 0) | ((Font::font_flags & FontTable::Flags_TripleWide) ? 2 : 0);

if (Font::font_flags & FontTable::Flags_InvertBitOrder) {
    glyphFlags |= 0x20;
    for (signed char char_m = Font::font_table.nCols - 1; char_m >= 0; char_m--)
    {
        TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
        // shift down to the next row of pixels for the character
        Font::now_y++;
        if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
        if (font_flags & FontTable::Flags_TripleHigh) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
    }
}
else if (Font::font_flags & FontTable::Flags_RotateBits) {
    for (unsigned char char_m = 0; char_m < 8; char_m++)
    {
        unsigned char rotatedMap = 0;
        for (unsigned char char_x = 0; char_x < Font::font_table.nCols; char_x++) {
            rotatedMap |= ((Font::font_table.table[char_i_x + char_x] & (1 << char_m)) ? 1 : 0) << char_x;
        }
        TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
        // shift down to the next row of pixels for the character
        Font::now_y++;
        if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
        if (font_flags & FontTable::Flags_TripleHigh) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, rotatedMap, 8, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
    }
}
else {
    for (unsigned char char_m = 0; char_m < Font::font_table.nCols; char_m++)
    {
        TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
        // shift down to the next row of pixels for the character
        Font::now_y++;
        if (font_flags & (FontTable::Flags_DoubleHigh | FontTable::Flags_TripleHigh)) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
        if (font_flags & FontTable::Flags_TripleHigh) {
            TFTLCD::draw_glyph(Font::now_x, Font::now_y, Font::font_color, Font::txt_backcolor, Font::font_table.table[char_i_x + char_m], Font::font_table.nCols, glyphFlags);
            // shift down to the next row of pixels for the character
            Font::now_y++;
        }
    }
}

光栅或位图字体规范

有许多字体规范,包括光栅化位图类型字体。这些规范不一定描述在诸如 KeDei TFT 库之类的应用程序中使用的字形位图,而是提供与设备无关的位图字体格式描述。

字形位图分布格式

“BITMAP”开始当前字形的位图。这条线后面必须跟 Y 轴上每个像素的一条线。在这个例子中,字形是 16 像素高,所以后面有 16 行。每行包含一行中像素的十六进制表示。“1”位表示渲染像素。每行四舍五入到 8 位(一个字节)边界,右侧用零填充。在这个例子中,字形正好是 8 个像素宽,因此每行正好占据 8 位(一个字节),因此没有填充。一行栅格数据的最高有效位代表最左边的像素。

Oracle 在 Solarix X Window System Developer's Guide, Chapter 4 Font Support at https://docs.oracle.com/cd/E19253-01/816-0279/6m6pd1cvk/index.html有一个表格列出了几种不同的位图字体格式,并且这就是说:

如表 4-4 所示,许多位图字体文件格式是依赖于体系结构的二进制文件。它们不能在不同架构的机器之间共享(例如,在 SPARC 和 IA 之间)。

PSF(PC 屏幕字体),一种二进制规范,描述于 URL https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html

PSF 代表 PC 屏幕字体。没有 Unicode 映射的 psf1 格式是 H. Peter Anvin 在 1989 年左右为他的 DOS 屏幕字体编辑器 FONTEDIT.EXE 设计的。1994 年 10 月,他添加了 Unicode 映射和程序 psfaddtable、psfgettable、psfstriptable 来操作它 - 参见 kbd-0.90。为了处理藏文,Andries Brouwer 在 1999 年 9 月增加了对 Unicode 值序列和 psf2 格式的支持 - 请参阅 kbd-1.00。

来自“早期 Microsoft 知识库文章存档”的 Microsoft Q65123 https://jeffpar.github.io/kbarchive/kb/065/Q65123/

Microsoft Windows 字体文件的格式是为光栅和矢量字体定义的。在某些 GDI 支持模块中,智能文本生成器可以使用这些格式。尤其是矢量格式,GDI 本身比支持模块更频繁地使用这些格式。

Metagraphics .fnt 字体文件规范https://www.metagraphics.com/metawindow/fonts/fnt-specs.htm

Microchip 图形库,Microchip 图形库中的 AN1182 字体 (PDF)

也可以看看

我在哪里可以找到 .fon 格式规范?

这个文件格式网站有几种不同字体规范的描述。https://docs.fileformat.com/font/fnt/

标签: fontsarduinobitmapbitmap-fonts

解决方案


光栅或位图字体以多种不同的方式表示,并且已经为 Linux 和 Windows 开发了位图字体文件标准。然而,编程语言源代码中位图字体的原始数据表示似乎因以下因素而异:

  • 目标计算机的内存架构,
  • 显示控制器的架构和通信路径,
  • 字符字形高度和宽度(以像素为单位)和
  • 位图存储的内存量以及采取了哪些措施使其尽可能小。

位图字体的简要概述

通用位图是一个数据块,其中各个位用于指示打开或关闭的状态。位图的一种用途是存储图像数据。字符字形可以创建和存储为图像集合,字符集中的每个字符一个,因此使用位图编码和存储每个字符图像是很自然的选择。

位图字体是用于指示如何通过打开或关闭像素或打印或不打印页面上的点来显示或打印字符的位图。参见维基百科位图字体

位图字体是将每个字形存储为像素数组(即位图)的字体。它很少被称为光栅字体或像素字体。位图字体只是字形光栅图像的集合。对于字体的每个变体,都有一组完整的字形图像,每组包含每个字符的图像。例如,如果一种字体有三种大小,以及粗体和斜体的任意组合,那么必须有 12 组完整的图像。

使用位图字体的简史

最早的用户界面终端(如电传终端)使用点阵打印机机制在纸卷上打印。随着阴极射线管终端的发展,位图字体很容易转移到该技术,因为发光点由扫描电子枪打开和关闭。

最早的位图字体具有固定的高度和宽度,位图作为一种印章或图案在输出介质、纸张或显像管上打印字符,具有固定的行高和固定的行宽,例如 80 列和DEC VT-100终端的24线。

随着处理能力的提高,一种更复杂的印刷方法变得可用,矢量字体用于提高显示的文本质量并提供改进的缩放,同时还减少了描述字符字形所需的内存。

此外,虽然点或像素矩阵在英语等语言中表现得相当好,但位图字体不能很好地服务于具有复杂字形形式的书面语言。

源代码中位图字体的表示

有许多位图字体文件格式提供了一种在与设备无关的描述中表示位图字体的方法。有关示例,请参阅Wikipedia 主题 - Glyph Bitmap Distribution Format

Adobe 的字形位图分发格式 (BDF) 是一种用于存储位图字体的文件格式。内容采用文本文件的形式,旨在供人类和计算机阅读。BDF 通常用于 Unix X Window 环境。它在很大程度上已被更高效的 PCF 字体格式以及可缩放字体(如 OpenType 和 TrueType 字体)所取代。

其他位图标准,例如 XBM、Wikipedia topic - X BitMap或 XPM、Wikipedia topic - X PixMap是描述位图的源代码组件,但是其中许多并不专门用于位图字体,而是用于其他图形图像,例如图标、光标, ETC。

由于位图字体是一种较旧的格式,很多时候位图字体都包含在另一种字体标准(如 TrueType)中,以便与现代操作系统(如 Linux 和 Windows)的标准字体子系统兼容。

然而,在裸机上运行或使用 RTOS 的嵌入式系统通常需要类似于 XBM 格式的原始位图字符图像数据。请参阅具有此示例的图形文件格式百科全书:

以下是使用 X10 和 X11 变体存储的 16x16 位图的示例。请注意,每个数组包含完全相同的数据,但使用不同的数据字类型存储:

/* XBM X10 format */
#define xlogo16_width 16
#define xlogo16_height 16

static unsigned short xlogo16_bits[] = {
  0x0f80, 0x1e80, 0x3c40, 0x7820, 0x7810, 0xf008, 0xe009, 0xc005,
  0xc002, 0x4007, 0x200f, 0x201e, 0x101e, 0x083c, 0x0478, 0x02f0};

/* XBM X11 format */
#define xlogo16_width 16
#define xlogo16_height 16

static unsigned char xlogo16_bits[] = {
    0x0f, 0x80, 0x1e, 0x80, 0x3c, 0x40, 0x78, 0x20, 0x78, 0x10,
    0xf0, 0x08, 0xe0, 0x09, 0xc0, 0x05, 0xc0, 0x02, 0x40, 0x07,
    0x20, 0x0f, 0x20, 0x1e, 0x10, 0x1e, 0x08, 0x3c, 0x04, 0x78,
    0x02, 0xf0};

遍历位图字体中每条数据的位的顺序对于实现所需的结果很重要。

因为每个像素只有一位大小,所以数组中的每个字节都包含八个像素的信息,位图中的第一个像素(位置 0,0)由数组中第一个字节的高位表示。如果图像宽度不是 8 的倍数,则不使用每行最后一个字节中的额外位并被忽略。

虽然这个描述似乎足够好,但“第一个字节的高位”的定义取决于机器架构,Big-endian 与 Little-endian。下图使用上述 X 徽标的 XBM 描述绘制,左侧显示在 Intel i7-7900 CPU 上从最高有效位到最低有效位遍历每个位图数据字节的位,而右侧则相反。

显示 XBM 16x16 图像 Big-endian 与 Little-endian

位图字体的注意事项

位图字体具有以像素或点为单位的单元格大小或字符高度和宽度。一行文本是在显示器上逐个像素地标记或绘制的一系列这些单元格。

由于位图字体的位图不是设备独立的,作为一系列数字,描述位图字体的字符图像的原始数据以及该数据如何存储在内存中以及如何被 CPU 访问是设备相关的。也可以转换位图数据以便更有效地使用机器资源。

位图字体的位图可以以这样一种方式存储,即它们节省了所使用的内存,同时需要额外的处理才能将像素正确地放置在绘图表面上。因此,为了完全使用 unsigned char 的 8 位,可以先存储 5 像素宽 x 8 像素高的字体,而不是先存储宽度。

除了旋转位图以更有效地使用字节中的位之外,还可以使用压缩算法来减少位图字体表所需的内存量。有关方法的讨论,请参阅Lightweight (de)compression algorithm for embedded use

机器架构可以以不同的方式物理访问表示像素的各个位。位图字体的原始数据为 Little-endian 机器布置的处理方式将与 Big-endian 机器的原始数据布置方式不同。请参阅 IBM 的这篇文章,用 C 编写与字节序无关的代码

拥有一个可视化位图字体并允许对位图字体数据进行各种转换以探索显示位图字符可能需要哪些更改的工具是有帮助的。例如,这里是我用来试验位图字体的 Windows GUI 应用程序,https://github.com/RichardChambers/utilities_tools/tree/main/fontshow


推荐阅读