1. 程式人生 > >數碼相框_電子書之程式碼閱讀及編寫(7)

數碼相框_電子書之程式碼閱讀及編寫(7)

數碼相框_電子書之程式碼閱讀及編寫(7)

 

在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_maneger.c管理檔案

管理3個字型子檔案:ascii.c(英文點陣),gbk.c(中文點陣),freetype(向量字型)

用來將獲取的字元編碼轉換為點陣資訊。

 

4、disp_manager.c管理檔案

管理2個顯示檔案:fb.c(LCD顯示) crt.c(串列埠顯示)

主要負責將點陣資訊傳送到視訊記憶體或串列埠上。

在3個管理.h標頭檔案裡,宣告3個不同的結構體

T_DispOpr:顯示操作結構體

T_FontOpr:字型操作結構體

T_EncodingOpr:編碼操作結構體

 

5、首先來寫顯示部分

fb.c需要用到fb初始化函式,以及顯示畫素函式,當我們換頁時,還需要一個清屏函式,所以有3個函式

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_managet.c裡

定義一個空連結串列:static PT_DispOpr g_ptDispOprHead = NULL;

寫一個Register DispOpr()函式,子檔案通過呼叫該函式來註冊到連結串列g_ptDispOprHead裡

 

在disp_manager.c裡

定義一個DisplayInit()函式,用來被main.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_managet.c檔案的disp_manager.c檔案的DisplayInit()呼叫,所以不能寫static了。

 

6、寫字型部分

和顯示部分思路一樣,在fonts_managet.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_managet.h裡的T_EncodingOpr結構體,宣告如下:

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_ptEncodingOprHeaad = 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(),並判斷支援哪種編碼,並獲取檔案第一個字元位置

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_ptDisOprHead顯示連結串列裡,找到name相同的結構體,並放入g_ptDispOpr結構體,然後執行g_ptDispOpr->DeviceInit();來初始化LCD

iError = SelectAndInitDisplay(acDisplay);

6、呼叫ShowNextPage()顯示第一頁

7、然後進入while(1)裡,通過getchar()來獲取命令列引數

輸入n,則呼叫ShowNextPage(),顯示下一頁

輸入u,則呼叫ShowNextPage(),顯示上一頁

輸入q退出