3.5" TFT LCD 上位图或光栅化字体位图和文本显示的差异
differences in bitmap or rasterized font bitmaps and text display on 3.5" TFT LCD
我使用的是 3.5: TFT LCD 显示器,带有 Arduino Uno 和制造商的库,即 KeDei TFT 库。该库附带了一种位图字体 table,对于 Arduino Uno 的少量内存来说非常庞大,所以我一直在寻找替代品。
我 运行 正在研究的是似乎没有标准表示法,我发现一些位图字体 tables 工作正常,而其他显示则很奇怪涂鸦和标记,或者它们颠倒显示,或者它们显示为翻转的字母。在写了一个简单的应用程序来显示一些字符后,我终于意识到不同的位图使用不同的字符方向。
我的问题
位图字体的位数据的规则或标准或预期表示是什么?为什么位图字体似乎有几种不同的文本字符方向?
关于这个问题的想法
这些是否是由于不同的目标设备造成的,例如 Windows 显示驱动程序或 Linux 显示驱动程序与裸机 Arduino TFT LCD 显示驱动程序?
用于将特定位图字体表示形式确定为一系列 unsigned char 值的标准是什么?不同类型的光栅设备,如TFT LCD显示器及其控制器,在通过设置像素颜色在显示表面上绘制时,是否具有不同的位序列?
还有哪些其他可能的位图字体表示需要我的库版本当前不提供的转换?
除了我用来确定需要什么转换的方法之外,还有其他方法吗?我目前将位图字体 table 插入测试程序并打印出一组字符以查看其外观,然后通过使用 Arduino 和 TFT LCD 屏幕进行测试来微调转换。
到目前为止我的经历
KeDei TFT 库带有一个位图字体 table,定义为
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 位图字体。
鉴于此 table 的大小和 Arduino Uno 上的少量内存,我开始寻找其他清晰易读且占用内存较少的位图字体。参见
我希望找到 6x6 位图字体,这样位图字体 table 的定义将从 const unsigned char font_table_16_col[96][16] = {
更改为 const unsigned char font_table_16_col[96][6] = {
,这将释放大量内存量。通过删除小写字母来减少 table 的实验表明这也有帮助。
寻找替代位图字体比我想象的要困难得多,设想有人在某处的 GitHub 存储库中拥有大量位图字体,通过一两次搜索很容易找到。
我 运行 的是,虽然我发现了几个不同的位图字体示例,但似乎并不是所有的都与我的特定 3.5" TFT LCD 显示器兼容。
例如,这里有四种不同的位图字体表示,显示两个字符的位图位,感叹号 (!) 和双引号 (")。5x8 似乎顺时针旋转了 90度数。8x8 和 16x8 的方向似乎正确,13x8 的方向似乎颠倒了。
生成上面的位图表示
上图中的位图字体表示,显示了文本字符方向的差异,由简单的 Windows GUI 生成,并用表示位值零和星号的破折号 (-) 显示(*) 表示位值 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;
}
位图字体table的前几行如下:
// 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" begins the bitmap for the current glyph. This line must be
followed by one line per pixel on the Y-axis. In this example the
glyph is 16 pixels tall, so 16 lines follow. Each line contains the
hexadecimal representation of pixels in a row. A "1" bit indicates a
rendered pixel. Each line is rounded to an 8 bit (one byte) boundary,
padded with zeroes on the right. In this example, the glyph is exactly
8 pixels wide, and so occupies exactly 8 bits (one byte) per line so
that there is no padding. The most significant bit of a line of raster
data represents the leftmost pixel.
Oracle in Solarix X Window System Developer's Guide, Chapter 4 Font Support at https://docs.oracle.com/cd/E19253-01/816-0279/6m6pd1cvk/index.html 有一个 table 列出了几种不同的位图字体格式并且是这样说的:
As illustrated in Table 4–4, many bitmap font file formats are
architecture-dependent binary files. They cannot be shared between
machines of different architectures (for example, between SPARC and
IA).
- 位图分布格式,.bdf 文件,非二进制,非特定架构
- Portable 编译格式,.pcf 文件,二进制,非体系结构特定
- Little Endian 预构建格式,二进制,特定于体系结构
- Big Endian 预构建格式,二进制,特定于体系结构
PSF(PC 屏幕字体),二进制规范,描述于 URL https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
PSF stands for PC Screen Font. The psf1 format without Unicode map was
designed by H. Peter Anvin in 1989 or so for his DOS screen font
editor FONTEDIT.EXE. In Oct 1994 he added the Unicode map and the
programs psfaddtable, psfgettable, psfstriptable to manipulate it -
see kbd-0.90. Andries Brouwer added support for sequences of Unicode
values and the psf2 format in Sep 1999 in order to handle Tibetan -
see kbd-1.00.
来自“早期 Microsoft 知识库文章存档”的 Microsoft Q65123 https://jeffpar.github.io/kbarchive/kb/065/Q65123/
Formats for Microsoft Windows font files are defined for both raster
and vector fonts. These formats can be used by smart text generators
in some GDI support modules. The vector formats, in particular, are
more frequently used by GDI itself than by support modules.
Metagraphics .fnt 字体文件规范https://www.metagraphics.com/metawindow/fonts/fnt-specs.htm
Microchip 图形库,AN1182 Fonts in the Microchip Graphics Library (PDF)
另见
Where I can find .fon format specification?
此文件格式网站描述了几种不同的字体规范。 https://docs.fileformat.com/font/fnt/
光栅或位图字体以多种不同的方式表示,并且已经为 Linux 和 Windows 开发了位图字体文件标准。然而,编程语言源代码中位图字体的原始数据表示似乎有所不同,具体取决于:
- 目标计算机的内存架构,
- 显示控制器的体系结构和通信路径,
- character glyph 高度和宽度(以像素为单位)和
- 用于位图存储的内存量以及采取什么措施使其尽可能小。
位图字体的简要概述
通用位图是一个数据块,其中各个位用于指示开或关状态。位图的一种用途是存储图像数据。字符字形可以作为一组图像创建和存储,字符集中的每个字符对应一个图像,因此使用位图来编码和存储每个字符图像是很自然的选择。
位图字体是用来指示如何通过打开或关闭像素或打印或不打印页面上的点来显示或打印字符的位图。参见 Wikipedia Bitmap fonts
A bitmap font is one that stores each glyph as an array of pixels
(that is, a bitmap). It is less commonly known as a raster font or a
pixel font. Bitmap fonts are simply collections of raster images of
glyphs. For each variant of the font, there is a complete set of glyph
images, with each set containing an image for each character. For
example, if a font has three sizes, and any combination of bold and
italic, then there must be 12 complete sets of images.
位图字体使用简史
最早的用户界面终端,如 teletype terminals used dot matrix printer mechanisms to print on rolls of paper. With the development of Cathode Ray Tube terminals 位图字体,很容易转移到该技术,因为发光点由扫描电子枪打开和关闭。
最早的位图字体是固定高度和宽度的,位图作为一种印章或图案在输出介质、纸张或显示管上打印字符,具有固定的行高和固定的线宽,例如作为 DEC VT-100 terminal.
的 80 列和 24 行
随着处理能力的提高,矢量字体可以使用更复杂的印刷方法来提高显示的文本质量并提供改进的缩放比例,同时减少描述字符字形所需的内存。
此外,虽然点或像素矩阵对于英语等语言来说效果很好,但位图字体对具有复杂字形形式的书面语言的效果很差。
位图字体在源代码中的表示
有许多位图字体文件格式提供了一种在设备独立描述中表示位图字体的方法。有关示例,请参见 Wikipedia topic - Glyph Bitmap Distribution Format
The Glyph Bitmap Distribution Format (BDF) by Adobe is a file format
for storing bitmap fonts. The content takes the form of a text file
intended to be human- and computer-readable. BDF is typically used in
Unix X Window environments. It has largely been replaced by the PCF
font format which is somewhat more efficient, and by scalable fonts
such as OpenType and TrueType fonts.
其他位图标准,如 XBM,Wikipedia topic - X BitMap, or XPM, Wikipedia topic - X PixMap,是描述位图的源代码组件,但其中许多并不是专门用于位图字体,而是其他图形图像,如图标、光标等。
由于位图字体是一种较旧的格式,很多时候位图字体都包含在另一种字体标准(例如 TrueType)中,以便与现代操作系统(例如 Linux 和 [=93)的标准字体子系统兼容=].
然而,运行 在裸机上或使用 RTOS 的嵌入式系统通常需要类似于 XBM 格式的原始位图字符图像数据。请参阅 Encyclopedia of Graphics File Formats,其中包含此示例:
Following is an example of a 16x16 bitmap stored using both its X10
and X11 variations. Note that each array contains exactly the same
data, but is stored using different data word types:
/* 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};
遍历位图字体中每段数据的位顺序对于实现所需结果很重要。
Because each pixel is only one bit in size, each byte in the array
contains the information for eight pixels, with the first pixel in the
bitmap (at position 0,0) represented by the high bit of the first byte
in the array. If the image width is not a multiple of eight, the extra
bits in the last byte of each row are not used and are ignored.
虽然这个描述看起来足够好,但“第一个字节的高位”的定义因机器架构而异,Big-endian versus Little-endian。下图使用上述 X 徽标的 XBM 描述绘制,左侧显示从最高有效位到最低有效位遍历 Intel i7-7900 CPU 上每个位图数据字节的位,右侧显示执行反转。
位图字体的注意事项
位图字体具有以像素或点为单位的单元格大小或字符高度和宽度。一行文本是在显示屏上逐个像素标记或绘制的一系列这些单元格。
由于位图字体的位图不是设备独立的,它是一系列数字,描述位图字体字符图像的原始数据以及该数据如何存储在内存中并由 CPU 访问是设备相关的.也可以转换位图数据以更有效地使用机器资源。
位图字体的位图可以以这样一种方式存储,即它们可以节省所使用的内存,同时需要额外的处理才能在绘图表面上正确放置像素。因此,5 像素宽 x 8 像素高的字体可以先存储高度而不是先存储宽度,以便完全使用 unsigned char 的 8 位。
除了旋转位图以更有效地使用字节中的位之外,还可以使用压缩算法来减少位图字体所需的内存量 table。有关方法的讨论,请参阅 。
机器架构可能会以不同的方式物理访问表示像素的各个位。为 Little-endian 机器处理而布置的位图字体的原始数据将不同于为 Big-endian 机器布置原始数据的方式。请参阅 IBM 的这篇文章 Writing endian-independent code in C。
拥有一个可视化位图字体并允许对位图字体数据进行各种转换以探索显示位图字符可能需要哪些更改的工具会很有帮助。例如,这是我用来试验位图字体的 Windows GUI 应用程序,https://github.com/RichardChambers/utilities_tools/tree/main/fontshow
我使用的是 3.5: TFT LCD 显示器,带有 Arduino Uno 和制造商的库,即 KeDei TFT 库。该库附带了一种位图字体 table,对于 Arduino Uno 的少量内存来说非常庞大,所以我一直在寻找替代品。
我 运行 正在研究的是似乎没有标准表示法,我发现一些位图字体 tables 工作正常,而其他显示则很奇怪涂鸦和标记,或者它们颠倒显示,或者它们显示为翻转的字母。在写了一个简单的应用程序来显示一些字符后,我终于意识到不同的位图使用不同的字符方向。
我的问题
位图字体的位数据的规则或标准或预期表示是什么?为什么位图字体似乎有几种不同的文本字符方向?
关于这个问题的想法
这些是否是由于不同的目标设备造成的,例如 Windows 显示驱动程序或 Linux 显示驱动程序与裸机 Arduino TFT LCD 显示驱动程序?
用于将特定位图字体表示形式确定为一系列 unsigned char 值的标准是什么?不同类型的光栅设备,如TFT LCD显示器及其控制器,在通过设置像素颜色在显示表面上绘制时,是否具有不同的位序列?
还有哪些其他可能的位图字体表示需要我的库版本当前不提供的转换?
除了我用来确定需要什么转换的方法之外,还有其他方法吗?我目前将位图字体 table 插入测试程序并打印出一组字符以查看其外观,然后通过使用 Arduino 和 TFT LCD 屏幕进行测试来微调转换。
到目前为止我的经历
KeDei TFT 库带有一个位图字体 table,定义为
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 位图字体。
鉴于此 table 的大小和 Arduino Uno 上的少量内存,我开始寻找其他清晰易读且占用内存较少的位图字体。参见
我希望找到 6x6 位图字体,这样位图字体 table 的定义将从 const unsigned char font_table_16_col[96][16] = {
更改为 const unsigned char font_table_16_col[96][6] = {
,这将释放大量内存量。通过删除小写字母来减少 table 的实验表明这也有帮助。
寻找替代位图字体比我想象的要困难得多,设想有人在某处的 GitHub 存储库中拥有大量位图字体,通过一两次搜索很容易找到。
我 运行 的是,虽然我发现了几个不同的位图字体示例,但似乎并不是所有的都与我的特定 3.5" TFT LCD 显示器兼容。
例如,这里有四种不同的位图字体表示,显示两个字符的位图位,感叹号 (!) 和双引号 (")。5x8 似乎顺时针旋转了 90度数。8x8 和 16x8 的方向似乎正确,13x8 的方向似乎颠倒了。
生成上面的位图表示
上图中的位图字体表示,显示了文本字符方向的差异,由简单的 Windows GUI 生成,并用表示位值零和星号的破折号 (-) 显示(*) 表示位值 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;
}
位图字体table的前几行如下:
// 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" begins the bitmap for the current glyph. This line must be followed by one line per pixel on the Y-axis. In this example the glyph is 16 pixels tall, so 16 lines follow. Each line contains the hexadecimal representation of pixels in a row. A "1" bit indicates a rendered pixel. Each line is rounded to an 8 bit (one byte) boundary, padded with zeroes on the right. In this example, the glyph is exactly 8 pixels wide, and so occupies exactly 8 bits (one byte) per line so that there is no padding. The most significant bit of a line of raster data represents the leftmost pixel.
Oracle in Solarix X Window System Developer's Guide, Chapter 4 Font Support at https://docs.oracle.com/cd/E19253-01/816-0279/6m6pd1cvk/index.html 有一个 table 列出了几种不同的位图字体格式并且是这样说的:
As illustrated in Table 4–4, many bitmap font file formats are architecture-dependent binary files. They cannot be shared between machines of different architectures (for example, between SPARC and IA).
- 位图分布格式,.bdf 文件,非二进制,非特定架构
- Portable 编译格式,.pcf 文件,二进制,非体系结构特定
- Little Endian 预构建格式,二进制,特定于体系结构
- Big Endian 预构建格式,二进制,特定于体系结构
PSF(PC 屏幕字体),二进制规范,描述于 URL https://www.win.tue.nl/~aeb/linux/kbd/font-formats-1.html
PSF stands for PC Screen Font. The psf1 format without Unicode map was designed by H. Peter Anvin in 1989 or so for his DOS screen font editor FONTEDIT.EXE. In Oct 1994 he added the Unicode map and the programs psfaddtable, psfgettable, psfstriptable to manipulate it - see kbd-0.90. Andries Brouwer added support for sequences of Unicode values and the psf2 format in Sep 1999 in order to handle Tibetan - see kbd-1.00.
来自“早期 Microsoft 知识库文章存档”的 Microsoft Q65123 https://jeffpar.github.io/kbarchive/kb/065/Q65123/
Formats for Microsoft Windows font files are defined for both raster and vector fonts. These formats can be used by smart text generators in some GDI support modules. The vector formats, in particular, are more frequently used by GDI itself than by support modules.
Metagraphics .fnt 字体文件规范https://www.metagraphics.com/metawindow/fonts/fnt-specs.htm
Microchip 图形库,AN1182 Fonts in the Microchip Graphics Library (PDF)
另见
Where I can find .fon format specification?
此文件格式网站描述了几种不同的字体规范。 https://docs.fileformat.com/font/fnt/
光栅或位图字体以多种不同的方式表示,并且已经为 Linux 和 Windows 开发了位图字体文件标准。然而,编程语言源代码中位图字体的原始数据表示似乎有所不同,具体取决于:
- 目标计算机的内存架构,
- 显示控制器的体系结构和通信路径,
- character glyph 高度和宽度(以像素为单位)和
- 用于位图存储的内存量以及采取什么措施使其尽可能小。
位图字体的简要概述
通用位图是一个数据块,其中各个位用于指示开或关状态。位图的一种用途是存储图像数据。字符字形可以作为一组图像创建和存储,字符集中的每个字符对应一个图像,因此使用位图来编码和存储每个字符图像是很自然的选择。
位图字体是用来指示如何通过打开或关闭像素或打印或不打印页面上的点来显示或打印字符的位图。参见 Wikipedia Bitmap fonts
A bitmap font is one that stores each glyph as an array of pixels (that is, a bitmap). It is less commonly known as a raster font or a pixel font. Bitmap fonts are simply collections of raster images of glyphs. For each variant of the font, there is a complete set of glyph images, with each set containing an image for each character. For example, if a font has three sizes, and any combination of bold and italic, then there must be 12 complete sets of images.
位图字体使用简史
最早的用户界面终端,如 teletype terminals used dot matrix printer mechanisms to print on rolls of paper. With the development of Cathode Ray Tube terminals 位图字体,很容易转移到该技术,因为发光点由扫描电子枪打开和关闭。
最早的位图字体是固定高度和宽度的,位图作为一种印章或图案在输出介质、纸张或显示管上打印字符,具有固定的行高和固定的线宽,例如作为 DEC VT-100 terminal.
的 80 列和 24 行随着处理能力的提高,矢量字体可以使用更复杂的印刷方法来提高显示的文本质量并提供改进的缩放比例,同时减少描述字符字形所需的内存。
此外,虽然点或像素矩阵对于英语等语言来说效果很好,但位图字体对具有复杂字形形式的书面语言的效果很差。
位图字体在源代码中的表示
有许多位图字体文件格式提供了一种在设备独立描述中表示位图字体的方法。有关示例,请参见 Wikipedia topic - Glyph Bitmap Distribution Format
The Glyph Bitmap Distribution Format (BDF) by Adobe is a file format for storing bitmap fonts. The content takes the form of a text file intended to be human- and computer-readable. BDF is typically used in Unix X Window environments. It has largely been replaced by the PCF font format which is somewhat more efficient, and by scalable fonts such as OpenType and TrueType fonts.
其他位图标准,如 XBM,Wikipedia topic - X BitMap, or XPM, Wikipedia topic - X PixMap,是描述位图的源代码组件,但其中许多并不是专门用于位图字体,而是其他图形图像,如图标、光标等。
由于位图字体是一种较旧的格式,很多时候位图字体都包含在另一种字体标准(例如 TrueType)中,以便与现代操作系统(例如 Linux 和 [=93)的标准字体子系统兼容=].
然而,运行 在裸机上或使用 RTOS 的嵌入式系统通常需要类似于 XBM 格式的原始位图字符图像数据。请参阅 Encyclopedia of Graphics File Formats,其中包含此示例:
Following is an example of a 16x16 bitmap stored using both its X10 and X11 variations. Note that each array contains exactly the same data, but is stored using different data word types:
/* 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};
遍历位图字体中每段数据的位顺序对于实现所需结果很重要。
Because each pixel is only one bit in size, each byte in the array contains the information for eight pixels, with the first pixel in the bitmap (at position 0,0) represented by the high bit of the first byte in the array. If the image width is not a multiple of eight, the extra bits in the last byte of each row are not used and are ignored.
虽然这个描述看起来足够好,但“第一个字节的高位”的定义因机器架构而异,Big-endian versus Little-endian。下图使用上述 X 徽标的 XBM 描述绘制,左侧显示从最高有效位到最低有效位遍历 Intel i7-7900 CPU 上每个位图数据字节的位,右侧显示执行反转。
位图字体的注意事项
位图字体具有以像素或点为单位的单元格大小或字符高度和宽度。一行文本是在显示屏上逐个像素标记或绘制的一系列这些单元格。
由于位图字体的位图不是设备独立的,它是一系列数字,描述位图字体字符图像的原始数据以及该数据如何存储在内存中并由 CPU 访问是设备相关的.也可以转换位图数据以更有效地使用机器资源。
位图字体的位图可以以这样一种方式存储,即它们可以节省所使用的内存,同时需要额外的处理才能在绘图表面上正确放置像素。因此,5 像素宽 x 8 像素高的字体可以先存储高度而不是先存储宽度,以便完全使用 unsigned char 的 8 位。
除了旋转位图以更有效地使用字节中的位之外,还可以使用压缩算法来减少位图字体所需的内存量 table。有关方法的讨论,请参阅
机器架构可能会以不同的方式物理访问表示像素的各个位。为 Little-endian 机器处理而布置的位图字体的原始数据将不同于为 Big-endian 机器布置原始数据的方式。请参阅 IBM 的这篇文章 Writing endian-independent code in C。
拥有一个可视化位图字体并允许对位图字体数据进行各种转换以探索显示位图字符可能需要哪些更改的工具会很有帮助。例如,这是我用来试验位图字体的 Windows GUI 应用程序,https://github.com/RichardChambers/utilities_tools/tree/main/fontshow