在LCD显示任意编码的文本文件,类似电子书

怎样在LCD上显示文件:

需要哪几个文件?

1.顶部文件

通过main.c分析命令行的操作,然后初始化各个管理文件下的结构体,比如DisplayInit();

然后进入draw.c,在draw.c里按顺序调用3个管理文件,并控制显示.

2. encoding_manager.c管理文件

管理4个编码子文件:utf-8.c  utf-16be.c  utf-16le.c  ascii.c

比如utf-8.c:判断某个文件是否以0xEF, 0xBB, 0xBF开头,若是,则接下来通过utf-8规律,来转换字节编码.

3. font_manager.c管理文件

管理3个字体子文件: ascii.c(英文点阵)  gbk.c(中文点阵)   freetype.c(矢量字体)

用来将获取的字符编码转换为点阵信息.

4. disp_manager.c管理文件

管理2个显示子文件:    fb.c(LCD显示)    crt.c(串口显示)

主要负责将点阵信息发送到显存或串口上.

在3个管理.h头文件里,声明3个不同的结构体

T_DispOpr :显示操作结构体

T_FontOpr:字体操作结构体

T_EncodingOpr:编码操作结构体

5.首先来写显示部分

fb.c需要用到fb初始化函数,以及显示像素函数,当我们换页时,还需要一个清屏函数,所以有个3个函数.

在disp_manager.h里的T_DispOpr结构体,声明如下:

typedef struct DispOpr {
char *name;
int iXres; //x像素个数
int iYres; //y像素个数
int iBpp; //每个像素多少位
int (*DeviceInit)(void); //该函数对于fb.c,是用来打开/dev/fb0,获取var和fix,然后mmap
int (*ShowPixel)(int iPenX, int iPenY, unsigned int dwColor); //显示一个像素点
int (*CleanScreen)(void); //清屏
struct DispOpr *ptNext; //指向下一个注册的T_DispOpr结构体
}T_DispOpr, *PT_DispOpr;

在disp_manager.c里

定义一个空链表: static PT_DispOpr g_ptDispOprHead = NULL;

写一个RegisterDispOpr()函数, 子文件通过调用该函数来注册到链表g_ptDispOprHead里

在disp_manager.c里

定义一个 DisplayInit()函数,用来被main.c初始化时调用。

在fb.c里

在fb.c里定义g_tFBOpr:

static T_DispOpr g_tFBOpr = {
.name = "fb",
.DeviceInit = FBDeviceInit, //该函数打开/dev/fb0,然后获取fix和var成员,来mmap()
.ShowPixel = FBShowPixel, //该函数根据x,y,color这3个函数参数,来显示一个像素点
.CleanScreen = FBCleanScreen, //该函数,通过memset来将显存清0
};

并定义一个FBInit ()函数,将结构体g_tFBOpr注册到g_ptDispOprHead链表里:

int FBInit(void)
{
return RegisterDispOpr(&g_tFBOpr);
}

由于FBInit()被disp_manager.c文件的DisplayInit()调用,所以不能写static了.

6.写字体部分

和显示部分思路一样,在fonts_manager.h里的声明了两个结构体,如下所示:

typedef struct FontBitMap {
int iXLeft; //文字最左边X坐标
int iYTop; //文字最顶部Y坐标
int iXMax; //文字的一行像素有多大
int iYMax; //文字的一列像素有多大
int iBpp; //像素格式
int iPitch; /* 对于单色位图, 两行象素之间的跨度,比如8*16,则跨度是8(文字的一行像素有8位) */
int iCurOriginX; //当前原点x坐标
int iCurOriginY; //当前原点y坐标
int iNextOriginX; //下个文字的原点x坐标
int iNextOriginY; //下个文字的原点y坐标
unsigned char *pucBuffer; //存放文字的像素数据
}T_FontBitMap, *PT_FontBitMap;
typedef struct FontOpr {
char *name;
int (*FontInit)(char *pcFontFile, unsigned int dwFontSize); //初始化字体文件
int (*GetFontBitmap)(unsigned int dwCode, PT_FontBitMap ptFontBitMap); //根据dwCode编码获取字体位图,并将信息(坐标,数据,格式等)存到ptFontBitMap里
struct FontOpr *ptNext;
}T_FontOpr, *PT_FontOpr;

在fonts_manager.c里

定义一个空链表:static PT_FontOpr g_ptFontOprHead = NULL;

写一个RegisterFontOpr()函数, 子文件通过调用该函数来注册到链表g_ptFontOprHead里

写一个GetFontOpr()函数,用来获取字体文件的name

写一个FontsInit ()函数,用来被main.c初始化时调用:

int FontsInit(void)
{
int iError;
iError = ASCIIInit(); //调用./fonts/ascii.c里的ASCIIInit()函数
if (iError)
{
DBG_PRINTF("ASCIIInit error!\n");
return -;
} iError = GBKInit(); //调用./fonts/gbk.c里的GBKInit ()函数
if (iError)
{
DBG_PRINTF("GBKInit error!\n");
return -;
} iError = FreeTypeInit(); //调用./fonts/freetype.c里的FreeTypeInit ()函数
if (iError)
{
DBG_PRINTF("FreeTypeInit error!\n");
return -;
}
return ;
}

写一个GetFontOpr()函数,该函数被编码文件调用,来使编码文件与字体文件关联起来,比如通过utf-8编码找到对应的freetype字体.

写字体文件,以freetype.c(矢量字体)为例

首先定义一个T_FontOpr结构体.

定义FreeTypeFontInit成员函数,初始化freetype库,设置字体大小等

定义GetFontBitmap成员函数,FT_Load_Char()转换位图,保存在PT_FontBitMap里

具体内容如下:

#include <config.h>
#include <fonts_manager.h>
#include <ft2build.h>
#include FT_FREETYPE_H
#include FT_GLYPH_H static int FreeTypeFontInit(char *pcFontFile, unsigned int dwFontSize);
static int FreeTypeGetFontBitmap(unsigned int dwCode, PT_FontBitMap ptFontBitMap); static T_FontOpr g_tFreeTypeFontOpr = {
.name = "freetype",
.FontInit = FreeTypeFontInit,
.GetFontBitmap = FreeTypeGetFontBitmap,
}; static FT_Library g_tLibrary;
static FT_Face g_tFace;
static FT_GlyphSlot g_tSlot; static int FreeTypeFontInit(char *pcFontFile, unsigned int dwFontSize)
{
int iError; /* 显示矢量字体 */
iError = FT_Init_FreeType(&g_tLibrary ); /* initialize library */
/* error handling omitted */
if (iError)
{
DBG_PRINTF("FT_Init_FreeType failed\n");
return -;
} iError = FT_New_Face(g_tLibrary, pcFontFile, , &g_tFace); /* create face object */
/* error handling omitted */
if (iError)
{
DBG_PRINTF("FT_Init_FreeType failed\n");
return -;
}
g_tSlot = g_tFace->glyph;
iError = FT_Set_Pixel_Sizes(g_tFace, dwFontSize, );
if (iError)
{
DBG_PRINTF("FT_Set_Pixel_Sizes failed : %d\n", dwFontSize);
return -;
}
return ;
} static int FreeTypeGetFontBitmap(unsigned int dwCode, PT_FontBitMap ptFontBitMap)
{
int iError;
int iPenX = ptFontBitMap->iCurOriginX; //初始值为:0 dwFontSize
int iPenY = ptFontBitMap->iCurOriginY;
#if 0
FT_Vector tPen;
tPen.x = ;
tPen.y = ; /* set transformation */
FT_Set_Transform(g_tFace, , &tPen);
#endif /* load glyph image into the slot (erase previous one) */
//iError = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER );
iError = FT_Load_Char(g_tFace, dwCode, FT_LOAD_RENDER | FT_LOAD_MONOCHROME);
if (iError)
{
DBG_PRINTF("FT_Load_Char error for code : 0x%x\n", dwCode);
return -;
} //DBG_PRINTF("iPenX = %d, iPenY = %d, bitmap_left = %d, bitmap_top = %d, width = %d, rows = %d\n", iPenX, iPenY, g_tSlot->bitmap_left, g_tSlot->bitmap_top, g_tSlot->bitmap.width, g_tSlot->bitmap.rows);
/*笛卡尔坐标的左上角是(bitmap_left,bitmap_top),
对应LCD的左上角是(Y+bitmap_left,Y-bitmap_top)*/
ptFontBitMap->iXLeft = iPenX + g_tSlot->bitmap_left;
ptFontBitMap->iYTop = iPenY - g_tSlot->bitmap_top;
ptFontBitMap->iXMax = ptFontBitMap->iXLeft + g_tSlot->bitmap.width;
ptFontBitMap->iYMax = ptFontBitMap->iYTop + g_tSlot->bitmap.rows;
ptFontBitMap->iBpp = ;
ptFontBitMap->iPitch = g_tSlot->bitmap.pitch;
ptFontBitMap->pucBuffer = g_tSlot->bitmap.buffer;
ptFontBitMap->iNextOriginX = iPenX + g_tSlot->advance.x / ;
ptFontBitMap->iNextOriginY = iPenY;
//DBG_PRINTF("iXLeft = %d, iYTop = %d, iXMax = %d, iYMax = %d, iNextOriginX = %d, iNextOriginY = %d\n", ptFontBitMap->iXLeft, ptFontBitMap->iYTop, ptFontBitMap->iXMax, ptFontBitMap->iYMax, ptFontBitMap->iNextOriginX, ptFontBitMap->iNextOriginY);
return ;
} int FreeTypeInit(void)
{
return RegisterFontOpr(&g_tFreeTypeFontOpr);
}

7.写编码部分

在encoding_manager.h里的T_EncondingOpr结构体,声明如下:

typedef struct EncodingOpr {
char *name;
int iHeadLen; //文件以多少字节开头
PT_FontOpr aptFontOprSupported[]; //指针数组,用来存放支持该编码的字体结构体,以后就通过这个来显示文字
int (*isSupport)(unsigned char *pucBufHead); //该函数判断要显示的文件是否支持XX格式
int (*GetCodeFrmBuf)(unsigned char *pucBufStart, unsigned char *pucBufEnd, unsigned int *pdwCode);
//将文件里的字节转为编码,存到*pdwCode里
struct EncodingOpr *ptNext; //链表
}T_EncodingOpr, *PT_EncodingOpr;

在encoding_manager.c里

定义一个空链表: static PT_EncodingOpr g_ptEncodingOprHead= NULL;

写一个RegisterEncodingOpr()函数, 子文件通过调用该函数来注册到链表g_ptEncodingOprHead里

写一个SelectEncodingOprForFile()函数,,通过链表来找isSupport成员函数,判断要显示的文字支持哪种格式

写一个EncodingInit()函数,调用每个编码文件的init()函数,里面会初始化编码T_EncodingOpr结构体,并添加编码所支持的文字结构体。

写编码文件,以utf-8.c为例

比如:

对于ansi.c(编码文件),其实就是GBK编码, ascii占1字节,使用ascii点阵库,汉字占2字节,使用HZK16汉字库.

对于utf-8.c(编码文件), ascii只占1字节, 使用ascii点阵库,而汉字占2~4字节,由于freetype字库默认支持的是utf-16格式,所以需要utf-8转换为utf-16后,再使用freetype字库,转换如下图所示:

8.写draw.c

8.1首先定义一个T_PageDesc结构体

用来控制分页换行用,需要用到双向链表

typedef struct PageDesc {

       int iPage;                                                  //当前页数

       unsigned char *pucLcdFirstPosAtFile;            //在LCD上第一个字符位置位于在文件哪个位置

       unsigned char *pucLcdNextPageFirstPosAtFile; //下一页的LCD上第一个字符位置位于文件哪位置

       struct PageDesc *ptPrePage;     //上一页链表,指向上一个T_PageDesc结构体

       struct PageDesc *ptNextPage;     //下一页链表,指向下一个T_PageDesc结构体

} T_PageDesc, *PT_PageDesc;

8.2 写一个OpenTextFile()函数

用来打开文本文件,然后mmap(),并判断支持哪种编码,并获取文件第一个字符位置g_pucLcdFirstPosAtFile标志量,该值被显示下一页函数使用

代码如下:

static int g_iFdTextFile;                       //文件描述符
static unsigned char *g_pucTextFileMem; //内存映射基地址
static unsigned char *g_pucTextFileMemEnd; //内存映射结尾地址
static PT_EncodingOpr g_ptEncodingOprForFile; //用来指向该文件支持的编码EncodingOpr结构体 static unsigned char *g_pucLcdFirstPosAtFile; //第一个字符位于文件的位置
int OpenTextFile(char *pcFileName)
{
g_iFdTextFile = open(pcFileName, O_RDONLY);
... ...
if(fstat(g_iFdTextFile, &tStat))
{
DBG_PRINTF("can't get fstat\n");
return -;
}
g_pucTextFileMem = (unsigned char *)mmap(NULL , tStat.st_size, PROT_READ, MAP_SHARED, g_iFdTextFile, );
g_pucTextFileMemEnd = g_pucTextFileMem + tStat.st_size; g_ptEncodingOprForFile = SelectEncodingOprForFile(g_pucTextFileMem); //获取支持的编码格式
if (g_ptEncodingOprForFile)
{
g_pucLcdFirstPosAtFile = g_pucTextFileMem + g_ptEncodingOprForFile->iHeadLen; //去掉文件编码前缀的开头位置
return ;
}
else
{
return -;
}
}

8.3 写一个ShowOnePage ()显示一页函数

首先设置原点xy为(0,fontsize),通过编码结构体的成员函数获取编码,判断编码是否为\r \n \t,然后通过字体结构体的成员函数将编码转为位图,然后判断是否换行,满页,最后显示

8.4 写一个SetTextDetail()函数

通过支持的编码,来设置HZK,freetype,ascii字体文件,以及文字大小,供给main.c调用

9.写main.c

main.c主要用来通过main.c分析命令行的操作,然后初始化各个管理文件下的结构体,比如DisplayInit();

命令行:

./show_file   [-l] [-s Size] [-d Dispshow] [-f freetype_font_file] [-h HZK] <text_file>

//-l  :列出选项

//-s  :设置文字大小

//-d :选择显示到哪里,是fb还是crt

//-f  :指定矢量文字文件位置

//-h  :指定汉字库文件位置

// text_file:指定要显示哪个文件 

main.c流程:

1.通过getopt(argc,argv, "ls:f:h:d:");来解析命令行,获取每个选项后的参数

2.然后调用管理文件的初始化函数,去初始化显示文件fb.c,字体文件freetype.c,gbk.c等,以及添加链表

比如:       iError = DisplayInit();      //最终调用FBInit();->RegisterDispOpr(&g_tFBOpr);

3.因为optind等于<text_file>位置,所以通过optind打开<text_file>文件:

strncpy(acTextFile, argv[optind], );
acTextFile[] = '\0';
iError = OpenTextFile(acTextFile); //里面进行mmap(),并获取该文件所支持的编码结构体

4.设置文本细节(HZK库,freetype库,文字大小)

iError = SetTextDetail(acHzkFile, acFreetypeFile, dwFontSize);   //该函数位于draw.c

5.调用SelectAndInitDisplay()函数,通过[-d Dispshow]来从g_ptDispOprHead显示链表里,找到name相同的结构体,并放入g_ptDispOpr结构体,然后执行g_ptDispOpr->DeviceInit(); 来初始化LCD

iError = SelectAndInitDisplay(acDisplay);

6.调用ShowNextPage()显示第一页

7.然后进入while(1)里,通过getchar()来获取命令行参数.

输入n则调用ShowNextPage(),显示下一页

输入u则调用ShowNextPage(),显示上一页

输入q退出

10.在PC上显示文本文件

输入ctrl+alt+tab+F1~F6  进入tty1~6字符终端界面,如下图所示,在tty1和tty2里登录book用户:

再次输入ctrl+alt+tab+F7  则退出字符终端界面,进入图形GUI界面

然后输入ps -A,可以发现tty1和tty2显示login登录状态,而其它tty显示getty待机状态:

接下来便通过svaglib库,使在字符终端界面上显示图形,这样就能显示文本了

10.1安装svgalib:

搜索ubuntu svgalib,找到下载地址https://launchpad.net/ubuntu/+source/svgalib/1:1.4.3-30:

svgalib_1.4.3.orig.tar.gz               //源码

svgalib_1.4.3-30.debian.tar.gz        // 在其debian/patches目录下有很多补丁文件

svgalib_1.4.3-30.dsc

打补丁:

tar xzf svgalib_1.4.3.orig.tar.gz

tar xzf svgalib_1.4.3-.debian.tar.gz

cd svgalib-1.4..orig/

for file in ../debian/patches/*.patch; do patch -p1 < $file; done     

编译安装:

sudo make install   // 编译出错,需要安装libx86

10.2 安装libx86:

搜索ubuntu libx86,找到下载地址http://packages.ubuntu.com/lucid/libx86-1:

tar xzf libx86_1.+ds1.orig.tar.gz 

gunzip libx86_1.+ds1-.diff.gz

cd libx86-1.1/

patch -p1 < ../libx86_1.+ds1-.diff 

make     // 出错

修改lrmi.c,添加宏, 参考561491.patch,添加如下代码:

=======================================

#if defined(__linux__) && !defined(TF_MASK)

#define TF_MASK X86_EFLAGS_TF

#define IF_MASK X86_EFLAGS_IF

#define VIF_MASK X86_EFLAGS_VIF

#define IOPL_MASK X86_EFLAGS_IOPL

#endif

=======================================

make
sudo make install //安装成功

10.3 应用程序示例

参考:http://www.svgalib.org/jay/beginners_guide/beginners_guide.html

svgatext.c代码如下:

#include "stdlib.h"
#include "vga.h"
#include "vgagl.h" int main(void)
{
int x, y; vga_init(); vga_setmode(G320x200x256); //分辨率为320x200,24位分辨率 gl_setpalettecolor(, 0xE7>>, 0xDB>>, 0xB5>>);
//将设置调色板第4格设为0xE7DBB5颜色 (泛黄色) vga_setcolor(); //使用调色板第4格的颜色 for (x = ; x < ; x++)
for (y = ; y < ; y++)
vga_drawpixel(x, y); //将整个屏设为调色板第4格的颜色 sleep();
vga_setmode(TEXT); //返回到字符终端界面上
return EXIT_SUCCESS;
}

编译svgatext.c

gcc  -o svgatext  svgatext.c   -lvga  -lvgal

10.4 参考svgatext.c,在电子书项目里的./fonts目录下写crt.c显示文件

11.自己写电子书

思路如下:

字体文件只要一个freetype.c,用来显示矢量字体

编码文件只需要: utf-16be.c,utf-16le.c, utf-8,iconv_encoding.c

显示文件就只要一个fb.c文件.

最后写一个draw.c、main.c文件

iconv_encoding.c作用:

因为freetype只支持unicode,所以先判断文本文件是否是GBK编码,若是,则通过iconv_encoding.c,将整个文本文件转换成utf-8编码.

过程:

在arm板上使用iconv()函数时,发现调用
iconv_open("utf-8", " GBK")失败,错误信息为“Invalid argument”.

后来才发现是libiconv库和arm板上的C库版本不同,参考: http://blog.csdn.net/love_life2011/article/details/7086910

进入http://ftp.gnu.org/gnu/libiconv/libiconv-1.14.tar.gz下载libiconv库,重新交叉编译

解压进入libconv目录:

$./configure --prefix=$PWD/out --host=arm-linux
$make
$make install

然后进入out子目录,将编译好的out/lib/目录下的preloadable_libiconv.so拷贝到arm板的lib目录下

再进入arm板,更新环境变量:

$ export LD_PRELOAD=/lib/preloadable_libiconv.so

5.数码相框-额外项目电子书总结,并使用svgalib库的更多相关文章

  1. Maven项目使用阿里云的Maven库

    Maven项目下载一些jar包非常慢,有时候一个项目能下一个上午,因此可以考虑使用阿里云的Maven库,因为是国内的,所以下载速度非常酷 单个项目使用阿里云的Maven库: pom文件中 <!- ...

  2. VS2012中使用CEGUI项目发布到XP平台的问题(核心方法就一句话。“你项目使用的所有外部依赖库都用/MT编译。”)

    接着上一篇文章,详细说说如何把一个带CEGUI的项目发布到XP平台. 这个问题纠缠了我好几天.这里把详细解决思路记下来.有同样问题的朋友可以少走很多弯路. 核心方法就一句话.“你项目使用的所有外部依赖 ...

  3. vue 项目中使用阿里巴巴矢量图标库iconfont

    原文:https://www.jianshu.com/p/38262f18eee2 1.打开iconfont阿里巴巴官网https://www.iconfont.cn 2.新建项目(这样方便后期维护图 ...

  4. 【vue.js】vue项目使用Iconfont(阿里图标库)

    vue项目使用Iconfont(阿里图标库) 2019-11-12  19:07:02  by冲冲 1.操作步骤 ① 登录阿里巴巴矢量图标库 https://www.iconfont.cn ,注册账号 ...

  5. Git 使用,本地项目上传到GitHub远程库

    Git 使用,本地项目上传到GitHub远程库 环境 GitHub账号 点此进入github官网 git客户端工具 点此进入git下载页 本地项目上传到 GitHub 在GitHub中创建一个仓库(远 ...

  6. iOS-启动项目(二)引入第三方库

    摘要 项目中很大几率会用到第三方库,通过 Pod 方式引入第三方库是效率很高的方式,这里介绍一个新的项目搭建 Pod 方式的环境,方便项目中引入第三方库文件. 刚创建的项目中如果需要用到第三方库,常用 ...

  7. 第八周课上额外项目:pwd的实现

    项目要求: 1 学习pwd命令 2 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 3 实现mypwd 4 测试mypwd 并且上交博客链接. 实验步骤 我首先不懂pwd到底是个 ...

  8. iOS 项目中用到的一些开源库和第三方组件

    iOS 项目中用到的一些 iOS 开源库和第三方组件 分享一下我目前所在公司 iOS 项目中用到的一些 iOS 开源库和第三方组件, 感谢开源, 减少了我们的劳动力, 节约了我们大量的时间, 让我们有 ...

  9. iOS项目中常用的第三方开源库

    1.项目使用的第三方开源库 项目使用了CocoaPods(类似java中的maven)管理常用的第三方库,一些特殊的单独引用,下面介绍下比较好用的几个. (1)AFNetworking 目前比较推荐的 ...

随机推荐

  1. C++ MFC棋牌类小游戏day6

    双人单机小游戏做完了,规则那部分还没介绍,暂时不打算介绍了,因为写的这个bug太多,我打算重新修改. 链接:https://pan.baidu.com/s/1XQKPSv0Tw36Qi2TeaRJiM ...

  2. Spring配置Bean,为属性赋值

    SayHello的实体类: package com.langchao; /** * @ClassName: SayHello * @description: * @author: ZhangYawei ...

  3. 背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理

    [源码下载] 背水一战 Windows 10 (79) - 自定义控件: Layout 系统, 控件模板, 事件处理 作者:webabcd 介绍背水一战 Windows 10 之 控件(自定义控件) ...

  4. MyBatis 源码分析 - 插件机制

    1.简介 一般情况下,开源框架都会提供插件或其他形式的拓展点,供开发者自行拓展.这样的好处是显而易见的,一是增加了框架的灵活性.二是开发者可以结合实际需求,对框架进行拓展,使其能够更好的工作.以 My ...

  5. 文件上传和WAF的攻与防

    Author:JoyChouDate:20180613 1. 前言 本文的测试环境均为 nginx/1.10.3 PHP 5.5.34 有些特性和 语言及webserver有关,有问题的地方,欢迎大家 ...

  6. JSP标准标签库:JSTL

    JSTL(JSP Standard Tag Library),JSP标准标签库,可以嵌入在jsp页面中使用标签的形式完成业务逻辑等功能. jstl出现的目的同el一样也是要代替jsp页面中的脚本代码. ...

  7. Eclipse中java内存溢出

    1.点击Window --->Preferences,如下图  

  8. Testing - 软件测试知识梳理 - 测试方法

    选择和使用测试方法和工具 按照测试需求用途(或测试技巧)选择 在软件开发生命周期和软件测试流程中适当地选择 按照测试人员实际技能选择 选择可提供的和可执行的 测试方法 类别及技巧 目标 使用方法 举例 ...

  9. 内存管理cpuset,mempolicy[原理]

    介绍cpuset,mbind,set_mempolicy在内存管理上的应用 change log :确定先从mempolicy的man 手册翻译开始研究,计划如下 .先从man手册入手,通过实现mem ...

  10. spring boot -thymeleaf-字符串操作

    以下是一些小测试 <span th:text="${#strings.length(hi)}" ></span> <span th:text=&quo ...