在LCD顯示任意編碼的文本文件,類似電子書 怎樣在LCD上顯示文件: 需要哪幾個文件? 1.頂部文件 通過main.c分析命令行的操作,然後初始化各個管理文件下的結構體,比如DisplayInit(); 然後進入draw.c,在draw.c里按順序調用3個管理文件,並控制顯示. 2. encodin ...
在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 -1; } iError = GBKInit(); //調用./fonts/gbk.c里的GBKInit ()函數 if (iError) { DBG_PRINTF("GBKInit error!\n"); return -1; } iError = FreeTypeInit(); //調用./fonts/freetype.c里的FreeTypeInit ()函數 if (iError) { DBG_PRINTF("FreeTypeInit error!\n"); return -1; } return 0; }
寫一個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 -1; } iError = FT_New_Face(g_tLibrary, pcFontFile, 0, &g_tFace); /* create face object */ /* error handling omitted */ if (iError) { DBG_PRINTF("FT_Init_FreeType failed\n"); return -1; } g_tSlot = g_tFace->glyph; iError = FT_Set_Pixel_Sizes(g_tFace, dwFontSize, 0); if (iError) { DBG_PRINTF("FT_Set_Pixel_Sizes failed : %d\n", dwFontSize); return -1; } return 0; } 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 = 0; tPen.y = 0; /* set transformation */ FT_Set_Transform(g_tFace, 0, &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 -1; } //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 = 1; ptFontBitMap->iPitch = g_tSlot->bitmap.pitch; ptFontBitMap->pucBuffer = g_tSlot->bitmap.buffer; ptFontBitMap->iNextOriginX = iPenX + g_tSlot->advance.x / 64; 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 0; } int FreeTypeInit(void) { return RegisterFontOpr(&g_tFreeTypeFontOpr); }
7.寫編碼部分
在encoding_manager.h里的T_EncondingOpr結構體,聲明如下:
typedef struct EncodingOpr { char *name; int iHeadLen; //文件以多少位元組開頭 PT_FontOpr aptFontOprSupported[4]; //指針數組,用來存放支持該編碼的字體結構體,以後就通過這個來顯示文字 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 -1; } g_pucTextFileMem = (unsigned char *)mmap(NULL , tStat.st_size, PROT_READ, MAP_SHARED, g_iFdTextFile, 0); g_pucTextFileMemEnd = g_pucTextFileMem + tStat.st_size; g_ptEncodingOprForFile = SelectEncodingOprForFile(g_pucTextFileMem); //獲取支持的編碼格式 if (g_ptEncodingOprForFile) { g_pucLcdFirstPosAtFile = g_pucTextFileMem + g_ptEncodingOprForFile->iHeadLen; //去掉文件編碼首碼的開頭位置 return 0; } else { return -1; } }
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], 128); acTextFile[127] = '\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-30.debian.tar.gz cd svgalib-1.4.3.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.1+ds1.orig.tar.gz gunzip libx86_1.1+ds1-6.diff.gz cd libx86-1.1/ patch -p1 < ../libx86_1.1+ds1-6.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(4, 0xE7>>2, 0xDB>>2, 0xB5>>2); //將設置調色板第4格設為0xE7DBB5顏色 (泛黃色) vga_setcolor(4); //使用調色板第4格的顏色 for (x = 0; x < 320; x++) for (y = 0; y < 200; y++) vga_drawpixel(x, y); //將整個屏設為調色板第4格的顏色 sleep(5); 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