数码相框-LCD显示多行文字
显示几行文字:
- 从左显示:先描边再算出边框。
- 居中显示:先算出边框,再确定坐标描画。
从左显示
第一行数据的起始位置是从(0,24)开始的。
要知道第二行数据从哪里开始,我们得知道画出来的矢量字体的边框是多少:
这个数据是笛卡尔坐标。
测试:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
int fd_fb;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;
break;
}
case 32:
{
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
/* Replace this function with something useful. */
void
draw_bitmap( FT_Bitmap* bitmap,
FT_Int x,
FT_Int y)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
//printf("x = %d, y = %d\n", x, y);
for ( i = x, p = 0; i < x_max; i++, p++ )
{
for ( j = y, q = 0; j < y_max; j++, q++ )
{
if ( i < 0 || j < 0 ||
i >= var.xres || j >= var.yres )
continue;
//image[j][i] |= bitmap->buffer[q * bitmap->width + p];
lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
}
}
}
int main(int argc, char **argv)
{
wchar_t *wstr1 = L"百问网gif";
wchar_t *wstr2 = L"www.100ask.net";
FT_Library library;
FT_Face face;
int error;
FT_Vector pen;
FT_GlyphSlot slot;
int i;
FT_BBox bbox;
FT_Glyph glyph;
int line_box_ymin = 10000;
int line_box_ymax = 0;
if (argc != 2)
{
printf("Usage : %s <font_file>\n", argv[0]);
return -1;
}
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
/* 清屏: 全部设为黑色 */
memset(fbmem, 0, screen_size);
/* 显示矢量字体 */
error = FT_Init_FreeType( &library ); /* initialize library */
/* error handling omitted */
error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
/* error handling omitted */
slot = face->glyph;
FT_Set_Pixel_Sizes(face, 24, 0);
/* 确定座标:
* lcd_x = 0
* lcd_y = 24
* 笛卡尔座标系:
* x = lcd_x = 0
* y = var.yres - lcd_y = var.yres - 24
*/
pen.x = 0 * 64;
pen.y = (var.yres - 24) * 64;
for (i = 0; i < wcslen(wstr1); i++)
{
/* set transformation */
FT_Set_Transform( face, 0, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, wstr1[i], FT_LOAD_RENDER );
if (error)
{
printf("FT_Load_Char error\n");
return -1;
}
error = FT_Get_Glyph( face->glyph, &glyph );
if (error)
{
printf("FT_Get_Glyph error!\n");
return -1;
}
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
if (line_box_ymin > bbox.yMin)
line_box_ymin = bbox.yMin;
if (line_box_ymax < bbox.yMax)
line_box_ymax = bbox.yMax;
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
var.yres - slot->bitmap_top);
/* increment pen position */
pen.x += slot->advance.x;
//pen.y += slot->advance.y;
}
/* 确定座标:
* lcd_x = 0
* lcd_y = line_box_ymax - line_box_ymin + 24
* 笛卡尔座标系:
* x = lcd_x = 0
* y = var.yres - lcd_y = var.yres - (line_box_ymax - line_box_ymin + 24)
*/
pen.x = 0 * 64;
pen.y = (var.yres - (line_box_ymax - line_box_ymin + 24)) * 64;
for (i = 0; i < wcslen(wstr2); i++)
{
/* set transformation */
FT_Set_Transform( face, 0, &pen);
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Char( face, wstr2[i], FT_LOAD_RENDER );
if (error)
{
printf("FT_Load_Char error\n");
return -1;
}
error = FT_Get_Glyph( face->glyph, &glyph );
if (error)
{
printf("FT_Get_Glyph error!\n");
return -1;
}
FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox );
if (line_box_ymin > bbox.yMin)
line_box_ymin = bbox.yMin;
if (line_box_ymax < bbox.yMax)
line_box_ymax = bbox.yMax;
draw_bitmap( &slot->bitmap,
slot->bitmap_left,
var.yres - slot->bitmap_top);
/* increment pen position */
pen.x += slot->advance.x;
//pen.y += slot->advance.y;
}
return 0;
}
-
包含必要的头文件。
-
定义全局变量:
-
fd_fb
: 存储Framebuffer设备文件的文件描述符。 -
var
: 结构体,用于存储当前屏幕变量信息(fb_var_screeninfo
)。 -
fix
: 结构体,用于存储当前屏幕固定信息(fb_fix_screeninfo
)。 -
screen_size
,fbmem
,line_width
,pixel_width
: 分别表示屏幕总大小、映射的Framebuffer内存指针、屏幕行宽度、像素宽度。
-
-
绘图函数:
-
lcd_put_pixel()
: 根据给定的颜色值和坐标,在Framebuffer上绘制一个像素。该函数处理不同的位深度(8, 16, 32),将颜色值转换为对应格式写入Framebuffer。 -
draw_bitmap()
: 将给定的FreeType位图字形数据绘制到Framebuffer上。遍历位图每个像素,根据位图尺寸、位置和Framebuffer坐标系将其颜色值写入Framebuffer。
-
-
主程序逻辑:
-
检查命令行参数,确保传入了字体文件路径。
-
初始化FreeType库并加载指定字体文件。
-
设置字体尺寸为24点。
-
第一行文字处理:
-
初始化
pen
(笔触位置)坐标为屏幕左下角(X=0, Y=24)。 -
遍历
wstr1
("百问网gif")中的每个字符:- 使用
FT_Set_Transform()
设置字符加载时的变换(仅使用pen
作为平移向量)。 - 加载字符并渲染到
face->glyph
(FT_Load_Char()
)。 - 调用
draw_bitmap()
将字符位图绘制到Framebuffer上,坐标为当前pen
位置。 - 更新
pen.x
(水平方向)以移动到下一个字符的位置(使用slot->advance.x
)。
- 使用
-
-
第二行文字处理:
- 初始化
pen
坐标为屏幕左下角,Y坐标根据第一行文字高度调整,但实际效果仍是左对齐显示第二行文字。 - 遍历
wstr2
("www.100ask.net")中的每个字符,重复第一行文字处理过程中的相同操作。 - 更新
pen.x
(水平方向)以移动到下一个字符的位置(使用slot->advance.x
)。
- 初始化
-
-
清理与退出:
- 不需要显式清理。
综上所述,这段代码通过FreeType库加载字体、处理字符串字形,直接使用字符加载后的位图信息(slot->bitmap
)在Framebuffer上从左到右逐字符绘制文字。第一行文字绘制完成后,第二行文字从屏幕左下角开始绘制,Y坐标基于第一行文字高度略有调整,实际效果是两行文字分别左对齐显示。
居中显示
MAX_GLYPHS
是指一行最多显示多少个字符。
会根据unicode从字体文件里面获取到glyph:
计算每个字框的边框信息,得出最大和最小:
TRUNCATE
取的单位是像素。
PIXELS
取的单位是1/64像素。
得出字框的大小:
从居中画出图像:
我们画出来的位置是在(0,0)的位置,要将原有的位置通过transform偏移到pen的位置。
测试:
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <linux/fb.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <wchar.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H
typedef struct TGlyph_ {
FT_UInt index; /* glyph index */
FT_Vector pos; /* glyph origin on the baseline */
FT_Glyph image; /* glyph image */
} TGlyph, *PGlyph;
#define MAX_GLYPHS 100
int fd_fb;
struct fb_var_screeninfo var; /* Current var */
struct fb_fix_screeninfo fix; /* Current fix */
int screen_size;
unsigned char *fbmem;
unsigned int line_width;
unsigned int pixel_width;
/* color : 0x00RRGGBB */
void lcd_put_pixel(int x, int y, unsigned int color)
{
unsigned char *pen_8 = fbmem+y*line_width+x*pixel_width;
unsigned short *pen_16;
unsigned int *pen_32;
unsigned int red, green, blue;
pen_16 = (unsigned short *)pen_8;
pen_32 = (unsigned int *)pen_8;
switch (var.bits_per_pixel)
{
case 8:
{
*pen_8 = color;
break;
}
case 16:
{
/* 565 */
red = (color >> 16) & 0xff;
green = (color >> 8) & 0xff;
blue = (color >> 0) & 0xff;
color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);
*pen_16 = color;
break;
}
case 32:
{
*pen_32 = color;
break;
}
default:
{
printf("can't surport %dbpp\n", var.bits_per_pixel);
break;
}
}
}
/* Replace this function with something useful. */
void
draw_bitmap( FT_Bitmap* bitmap,
FT_Int x,
FT_Int y)
{
FT_Int i, j, p, q;
FT_Int x_max = x + bitmap->width;
FT_Int y_max = y + bitmap->rows;
//printf("x = %d, y = %d\n", x, y);
for ( i = x, p = 0; i < x_max; i++, p++ )
{
for ( j = y, q = 0; j < y_max; j++, q++ )
{
if ( i < 0 || j < 0 ||
i >= var.xres || j >= var.yres )
continue;
//image[j][i] |= bitmap->buffer[q * bitmap->width + p];
lcd_put_pixel(i, j, bitmap->buffer[q * bitmap->width + p]);
}
}
}
int Get_Glyphs_Frm_Wstr(FT_Face face, wchar_t * wstr, TGlyph glyphs[])
{
int n;
PGlyph glyph = glyphs;
int pen_x = 0;
int pen_y = 0;
int error;
FT_GlyphSlot slot = face->glyph;;
for (n = 0; n < wcslen(wstr); n++)
{
glyph->index = FT_Get_Char_Index( face, wstr[n]);
/* store current pen position */
glyph->pos.x = pen_x;
glyph->pos.y = pen_y;
/* load时是把glyph放入插槽face->glyph */
error = FT_Load_Glyph(face, glyph->index, FT_LOAD_DEFAULT);
if ( error )
continue;
error = FT_Get_Glyph(face->glyph, &glyph->image );
if ( error )
continue;
/* translate the glyph image now */
/* 这使得glyph->image里含有位置信息 */
FT_Glyph_Transform(glyph->image, 0, &glyph->pos );
pen_x += slot->advance.x; /* 1/64 point */
/* increment number of glyphs */
glyph++;
}
/* count number of glyphs loaded */
return (glyph - glyphs);
}
void compute_string_bbox(TGlyph glyphs[], FT_UInt num_glyphs, FT_BBox *abbox )
{
FT_BBox bbox;
int n;
bbox.xMin = bbox.yMin = 32000;
bbox.xMax = bbox.yMax = -32000;
for ( n = 0; n < num_glyphs; n++ )
{
FT_BBox glyph_bbox;
FT_Glyph_Get_CBox(glyphs[n].image, FT_GLYPH_BBOX_TRUNCATE, &glyph_bbox );
if (glyph_bbox.xMin < bbox.xMin)
bbox.xMin = glyph_bbox.xMin;
if (glyph_bbox.yMin < bbox.yMin)
bbox.yMin = glyph_bbox.yMin;
if (glyph_bbox.xMax > bbox.xMax)
bbox.xMax = glyph_bbox.xMax;
if (glyph_bbox.yMax > bbox.yMax)
bbox.yMax = glyph_bbox.yMax;
}
*abbox = bbox;
}
void Draw_Glyphs(TGlyph glyphs[], FT_UInt num_glyphs, FT_Vector pen)
{
int n;
int error;
for (n = 0; n < num_glyphs; n++)
{
FT_Glyph_Transform(glyphs[n].image, 0, &pen);
/* convert glyph image to bitmap (destroy the glyph copy!) */
error = FT_Glyph_To_Bitmap(&glyphs[n].image, FT_RENDER_MODE_NORMAL, 0, /* no additional translation */
1 ); /* destroy copy in "image" */
if ( !error )
{
FT_BitmapGlyph bit = (FT_BitmapGlyph)glyphs[n].image;
draw_bitmap(&bit->bitmap, bit->left, var.yres - bit->top);
FT_Done_Glyph(glyphs[n].image );
}
}
}
int main(int argc, char **argv)
{
wchar_t *wstr1 = L"百问网gif";
wchar_t *wstr2 = L"www.100ask.net";
FT_Library library;
FT_Face face;
int error;
FT_Vector pen;
FT_GlyphSlot slot;
int i;
FT_BBox bbox;
int line_box_ymin = 10000;
int line_box_ymax = 0;
int line_box_width;
int line_box_height;
TGlyph glyphs[MAX_GLYPHS]; /* glyphs table */
FT_UInt num_glyphs;
if (argc != 2)
{
printf("Usage : %s <font_file>\n", argv[0]);
return -1;
}
fd_fb = open("/dev/fb0", O_RDWR);
if (fd_fb < 0)
{
printf("can't open /dev/fb0\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_VSCREENINFO, &var))
{
printf("can't get var\n");
return -1;
}
if (ioctl(fd_fb, FBIOGET_FSCREENINFO, &fix))
{
printf("can't get fix\n");
return -1;
}
line_width = var.xres * var.bits_per_pixel / 8;
pixel_width = var.bits_per_pixel / 8;
screen_size = var.xres * var.yres * var.bits_per_pixel / 8;
fbmem = (unsigned char *)mmap(NULL , screen_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd_fb, 0);
if (fbmem == (unsigned char *)-1)
{
printf("can't mmap\n");
return -1;
}
/* 清屏: 全部设为黑色 */
memset(fbmem, 0, screen_size);
/* 显示矢量字体 */
error = FT_Init_FreeType( &library ); /* initialize library */
/* error handling omitted */
error = FT_New_Face( library, argv[1], 0, &face ); /* create face object */
/* error handling omitted */
slot = face->glyph;
FT_Set_Pixel_Sizes(face, 24, 0);
/* wstr1 */
num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr1, glyphs);
compute_string_bbox(glyphs, num_glyphs, &bbox);
line_box_width = bbox.xMax - bbox.xMin;
line_box_height = bbox.yMax - bbox.yMin;
pen.x = (var.xres - line_box_width)/2 * 64;
pen.y = (var.yres - line_box_height)/2 * 64;
Draw_Glyphs(glyphs, num_glyphs, pen);
/* wstr2 */
num_glyphs = Get_Glyphs_Frm_Wstr(face, wstr2, glyphs);
compute_string_bbox(glyphs, num_glyphs, &bbox);
line_box_width = bbox.xMax - bbox.xMin;
line_box_height = bbox.yMax - bbox.yMin;
pen.x = (var.xres - line_box_width)/2 * 64;
pen.y = pen.y - 24 * 64;
Draw_Glyphs(glyphs, num_glyphs, pen);
return 0;
}
这段C代码使用FreeType库来在Linux Framebuffer上居中显示两行文字。以下是代码实现这一功能的主要步骤:
-
包含必要的头文件:
-
sys/mman.h
,sys/types.h
,sys/stat.h
,unistd.h
,linux/fb.h
,fcntl.h
,stdio.h
,string.h
,math.h
,wchar.h
- FreeType库相关头文件:
ft2build.h
,FT_FREETYPE_H
,FT_GLYPH_H
-
-
定义结构体和常量:
- 定义一个名为
TGlyph
的结构体,用于存储单个字形的索引、基线上的起始位置以及字形图像。 - 定义
MAX_GLYPHS
常量,表示最大字形数量。
- 定义一个名为
-
初始化Framebuffer:
- 打开
/dev/fb0
设备文件并获取文件描述符(fd_fb
)。 - 使用
ioctl()
系统调用获取屏幕变量信息(FBIOGET_VSCREENINFO
)和固定屏幕信息(FBIOGET_FSCREENINFO
)。 - 计算屏幕行宽度、像素宽度以及屏幕总大小。
- 使用
mmap()
系统调用映射Framebuffer内存到进程地址空间。
- 打开
-
定义绘图函数:
-
lcd_put_pixel()
: 根据给定的颜色值和坐标,在Framebuffer上绘制一个像素。该函数处理不同的位深度(8, 16, 32),将颜色值转换为对应格式写入Framebuffer。 -
draw_bitmap()
: 将给定的FreeType位图字形数据绘制到Framebuffer上。遍历位图每个像素,根据位图尺寸、位置和Framebuffer坐标系将其颜色值写入Framebuffer。
-
-
FreeType操作函数:
-
Get_Glyphs_Frm_Wstr()
: 根据输入的宽字符字符串和已加载的FreeType字体面,获取每个字符对应的字形信息,并存储在TGlyph
数组中。同时更新当前笔触位置(pen_x
和pen_y
)。 -
compute_string_bbox()
: 计算给定字形数组的边界框(FT_BBox
)。遍历字形数组,获取每个字形的边界框并累加,得到整个字符串的最小/最大X/Y坐标。 -
Draw_Glyphs()
: 将字形数组中的每个字形转换为位图,计算其在Framebuffer上的位置(pen
),并调用draw_bitmap()
进行绘制。完成后释放字形资源。
-
-
主程序逻辑:
-
检查命令行参数,确保传入了字体文件路径。
-
初始化FreeType库并加载指定字体文件。
-
设置字体尺寸为24点。
-
第一行文字处理:
- 获取
wstr1
("百问网gif")对应的字形数组,并计算其边界框。 - 计算居中位置:根据屏幕宽度减去字符串宽度后除以2,乘以64(转换为FreeType单位)。
- 使用计算出的居中位置绘制字形数组。
- 获取
-
第二行文字处理:
- 与第一行类似,获取
wstr2
("www.100ask.net")的字形数组及边界框。-mp8h446bu0hu8nq1g2v2b3bdt20bpu6c./) - 计算居中位置,但这次将Y坐标减去一行文字的高度(24 * 64),以实现垂直居中偏移。
- 绘制字形数组。
- 与第一行类似,获取
-
-
清理与退出:
- 不需要显式清理,因为映射的Framebuffer内存会在程序退出时自动解除映射,且FreeType库会在程序结束时自动释放资源。
综上所述,这段代码通过FreeType库加载字体、处理字符串字形,计算两行文字各自的边界框,然后确定每行文字在Framebuffer上的居中位置。最后,它遍历字形数组,将每个字形转换为位图并在Framebuffer上绘制,从而实现两行文字的居中显示。