Draw.c
15年8月23日13:54:14
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <config.h>
#include <draw.h>
#include <encoding_manager.h>
#include <fonts_manager.h>
#include <disp_manager.h>
#include <string.h>
typedef struct PageDesc {
int iPage;
unsigned char *pucLcdFirstPosAtFile;
unsigned char *pucLcdNextPageFirstPosAtFile;
struct PageDesc *ptPrePage;
struct PageDesc *ptNextPage;
} T_PageDesc, *PT_PageDesc;
static int g_iFdTextFile;
static unsigned char *g_pucTextFileMem;
static unsigned char *g_pucTextFileMemEnd;
static PT_EncodingOpr g_ptEncodingOprForFile;
static PT_DispOpr g_ptDispOpr;
static unsigned char *g_pucLcdFirstPosAtFile;
static unsigned char *g_pucLcdNextPosAtFile;
static int g_dwFontSize;
static PT_PageDesc g_ptPages = NULL;
static PT_PageDesc g_ptCurPage = NULL;
int OpenTextFile(char *pcFileName)
{
struct stat tStat;
g_iFdTextFile = open(pcFileName, O_RDONLY);
if (0 > g_iFdTextFile)
{
DBG_PRINTF("can't open text file %s\n", pcFileName);
return -1;
}
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);
if (g_pucTextFileMem == (unsigned char *)-1)
{
DBG_PRINTF("can't mmap for text file\n");
return -1;
}
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;
}
}
int SetFontsDetail(char *pcHZKFile, char *pcFileFreetype, unsigned int dwFontSize)
{
int iError = 0;
PT_FontOpr ptFontOpr;
PT_FontOpr ptTmp;
int iRet = -1;
g_dwFontSize = dwFontSize;
ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
while (ptFontOpr)
{
if (strcmp(ptFontOpr->name, "ascii") == 0)
{
iError = ptFontOpr->FontInit(NULL, dwFontSize);
}
else if (strcmp(ptFontOpr->name, "gbk") == 0)
{
iError = ptFontOpr->FontInit(pcHZKFile, dwFontSize);
}
else
{
iError = ptFontOpr->FontInit(pcFileFreetype, dwFontSize);
}
DBG_PRINTF("%s, %d\n", ptFontOpr->name, iError);
ptTmp = ptFontOpr->ptNext;
if (iError == 0)
{
/* 比如对于ascii编码的文件, 可能用ascii字体也可能用gbk字体,
* 所以只要有一个FontInit成功, SetTextDetail最终就返回成功
*/
iRet = 0;
}
else
{
DelFontOprFrmEncoding(g_ptEncodingOprForFile, ptFontOpr);
}
ptFontOpr = ptTmp;
}
return iRet;
}
int SelectAndInitDisplay(char *pcName)
{
int iError;
g_ptDispOpr = GetDispOpr(pcName);
if (!g_ptDispOpr)
{
return -1;
}
iError = g_ptDispOpr->DeviceInit();
return iError;
}
int IncLcdX(int iX)
{
if (iX + 1 < g_ptDispOpr->iXres)
return (iX + 1);
else
return 0;
}
int IncLcdY(int iY)
{
if (iY + g_dwFontSize < g_ptDispOpr->iYres)
return (iY + g_dwFontSize);
else
return 0;
}
int RelocateFontPos(PT_FontBitMap ptFontBitMap)
{
int iLcdY;
int iDeltaX;
int iDeltaY;
if (ptFontBitMap->iYMax > g_ptDispOpr->iYres)
{
/* 满页了 */
return -1;
}
/* 超过LCD最右边 */
if (ptFontBitMap->iXMax > g_ptDispOpr->iXres)
{
/* 换行 */
iLcdY = IncLcdY(ptFontBitMap->iCurOriginY);
if (0 == iLcdY)
{
/* 满页了 */
return -1;
}
else
{
/* 没满页 */
iDeltaX = 0 - ptFontBitMap->iCurOriginX;
iDeltaY = iLcdY - ptFontBitMap->iCurOriginY;
ptFontBitMap->iCurOriginX += iDeltaX;
ptFontBitMap->iCurOriginY += iDeltaY;
ptFontBitMap->iNextOriginX += iDeltaX;
ptFontBitMap->iNextOriginY += iDeltaY;
ptFontBitMap->iXLeft += iDeltaX;
ptFontBitMap->iXMax += iDeltaX;
ptFontBitMap->iYTop += iDeltaY;
ptFontBitMap->iYMax += iDeltaY;;
return 0;
}
}
return 0;
}
int ShowOneFont(PT_FontBitMap ptFontBitMap)
{
int x;
int y;
unsigned char ucByte = 0;
int i = 0;
int bit;
if (ptFontBitMap->iBpp == 1)
{
for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax;y++)
{
i = (y - ptFontBitMap->iYTop) * ptFontBitMap->iPitch;
for (x = ptFontBitMap->iXLeft, bit = 7; x < ptFontBitMap- >iXMax; x++)
{
if (bit == 7)
{
ucByte = ptFontBitMap->pucBuffer[i++];
}
if (ucByte & (1<<bit))
{
g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
}
else
{
/* 使用背景色, 不用描画 */
// g_ptDispOpr->ShowPixel(x, y, 0);
}
bit--;
if (bit == -1)
{
bit = 7;
}
}
}
}
else if (ptFontBitMap->iBpp == 8)
{
for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax;y++)
for (x = ptFontBitMap->iXLeft; x < ptFontBitMap->iXMax; x++)
{
//g_ptDispOpr->ShowPixel(x, y, ptFontBitMap- >pucBuffer[i++]);
if (ptFontBitMap->pucBuffer[i++])
g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
}
}
else
{
DBG_PRINTF("ShowOneFont error, can't support %d bpp\n", ptFontBitMap->iBpp);
return -1;
}
return 0;
}
int ShowOnePage(unsigned char *pucTextFileMemCurPos)
{
int iLen;
int iError;
unsigned char *pucBufStart;
unsigned int dwCode;
PT_FontOpr ptFontOpr;
T_FontBitMap tFontBitMap;
int bHasNotClrSceen = 1;
int bHasGetCode = 0;
tFontBitMap.iCurOriginX = 0;
tFontBitMap.iCurOriginY = g_dwFontSize;
pucBufStart = pucTextFileMemCurPos;
while (1)
{
iLen = g_ptEncodingOprForFile->GetCodeFrmBuf(pucBufStart, g_pucTextFileMemEnd, &dwCode);
if (0 == iLen)
{
/* 文件结束 */
if (!bHasGetCode)
{
return -1;
}
else
{
return 0;
}
}
bHasGetCode = 1;
pucBufStart += iLen;
/* 有些文本, \n\r两个一起才表示回车换行
* 碰到这种连续的\n\r, 只处理一次*/
if (dwCode == '\n')
{
g_pucLcdNextPosAtFile = pucBufStart;
/* 回车换行 */
tFontBitMap.iCurOriginX = 0;
tFontBitMap.iCurOriginY = IncLcdY(tFontBitMap.iCurOriginY);
if (0 == tFontBitMap.iCurOriginY)
{
/* 显示完当前一屏了 */
return 0;
}
else
{
continue;
}
}
else if (dwCode == '\r')
{
continue;
}
else if (dwCode == '\t')
{
/* TAB键用一个空格代替 */
dwCode = ' ';
}
DBG_PRINTF("dwCode = 0x%x\n", dwCode);
ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
while (ptFontOpr)
{
iError = ptFontOpr->GetFontBitmap(dwCode, &tFontBitMap);
if (0 == iError)
{
if (RelocateFontPos(&tFontBitMap))
{
/* 剩下的LCD空间不能满足显示这个字符 */
return 0;
}
if (bHasNotClrSceen)
{
/* 首先清屏
g_ptDispOpr->CleanScreen(COLOR_BACKGROUND);
bHasNotClrSceen = 0;
}
/* 显示一个字符 */
if (ShowOneFont(&tFontBitMap))
{
return -1;
}
tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
g_pucLcdNextPosAtFile = pucBufStart;
/* 继续取出下一个编码来显示 */
break;
}
ptFontOpr = ptFontOpr->ptNext;
}
}
return 0;
}
static void RecordPage(PT_PageDesc ptPageNew)
{
PT_PageDesc ptPage;
if (!g_ptPages)
{
g_ptPages = ptPageNew;
}
else
{
ptPage = g_ptPages;
while (ptPage->ptNextPage)
{
ptPage = ptPage->ptNextPage;
}
ptPage->ptNextPage = ptPageNew;
ptPageNew->ptPrePage = ptPage;
}
}
int ShowNextPage(void)
{
int iError;
PT_PageDesc ptPage;
unsigned char *pucTextFileMemCurPos;
if (g_ptCurPage)
{
pucTextFileMemCurPos = g_ptCurPage->pucLcdNextPageFirstPosAtFile;
}
else
{
pucTextFileMemCurPos = g_pucLcdFirstPosAtFile;
}
iError = ShowOnePage(pucTextFileMemCurPos);
DBG_PRINTF("%s %d, %d\n", __FUNCTION__, __LINE__, iError);
if (iError == 0)
{
if (g_ptCurPage && g_ptCurPage->ptNextPage)
{
g_ptCurPage = g_ptCurPage->ptNextPage;
return 0;
}
ptPage = malloc(sizeof(T_PageDesc));
if (ptPage)
{
ptPage->pucLcdFirstPosAtFile = pucTextFileMemCurPos;
ptPage->pucLcdNextPageFirstPosAtFile = g_pucLcdNextPosAtFile;
ptPage->ptPrePage = NULL;
ptPage->ptNextPage = NULL;
g_ptCurPage = ptPage;
RecordPage(ptPage);
return 0;
}
else
{
return -1;
}
}
return iError;
}
int ShowPrePage(void)
{
int iError;
DBG_PRINTF("%s %d\n", __FUNCTION__, __LINE__);
if (!g_ptCurPage || !g_ptCurPage->ptPrePage)
{
return -1;
}
iError = ShowOnePage(g_ptCurPage->ptPrePage->pucLcdFirstPosAtFile);
if (iError == 0)
{
DBG_PRINTF("%s %d\n", __FUNCTION__, __LINE__);
g_ptCurPage = g_ptCurPage->ptPrePage;
}
return iError;
}
下面来分析这段代码:
(1)首先是这个 PageDesc结构体:
typedef struct PageDesc {
int iPage;
unsigned char *pucLcdFirstPosAtFile;
unsigned char *pucLcdNextPageFirstPosAtFile;
struct PageDesc *ptPrePage;
struct PageDesc *ptNextPage;
} T_PageDesc, *PT_PageDesc;
电子书里面需要上下翻页,这时候,就需要记录下来每一页第一个显示的位置,以及下一页第一个显示的位置。
(2)static unsigned char *g_pucTextFileMemEnd;
为什么要定义这个MemEnd变量呢?这个变量主要是在GetCodeFrmBuf函数中使用,用来确定取码的时候,是否到了文件的最后。
(3)下面来看第一个函数:
int OpenTextFile(char * pcFileName)
{
struct stat tStat;
/* 用open函数来打开文件,对返回值进行判定 */
g_iFdTextFile = open(pcFileName, O_RDONLY);
if (g_iFdTextFile < 0)
{
DBG_PRINTF("Open %s failed!\n", pcFileName);
return -1;
}
/* 用fstat函数来获取文件的一些统计信息,并存在tStat这个结构体中 */
if (fstat(g_iFdTextFile, &tStat))
{
DBG_PRINTF("cannot get fstat!\n");
return -1;
}
/* 将文件映射到内存中,这样操作起来方便,用mmap函数映射以后,是返回指向这一块内存首地址 * 的一个指针 */
g_pucTextFileMem = (unsigned char *)mmap(NULL, tStat.st_size, PROT_READ, MAP_SHARED, g_iFdTextFile, 0);
if (g_pucTextFileMem == (unsigned char *)-1) /* 如果函数返回-1说明出错 */
{
DBG_PRINTF("cannot mmap for text file!\n");
return -1;
}
/* 计算出来mem最后的位置,用于确定取码的最后位置 */
g_pucTextFileMemEnd = g_pucTextFileMem + tStat.st_size;
/* 根据映射到内存中的文件内容,来选择一种支持的编码方式,成功返回一个结构体,失败返回NULL */
g_ptEncodingOprForFile = SelectEncodingOprForFile(g_pucTextFileMem);
if (g_ptEncodingOprForFile)
{
/* 选定编码方式以后,也就确定了这种编码方式的iHeadLen,根据g_pucTextFileMem * 和g_ptEncodingOprForFile->iHeadLen这两项,就可以确定想要在LCD上面显示 * 的第一个字符编码在内存中的位置了。每个编码格式的头部不同,如果为ascii,头部为 * 0,如果为utf-8,头部就为3.为了获得真正的编码,应该将头部舍去。
*/
g_pucLcdFirstPosAtFile = g_pucTextFileMem +
g_ptEncodingOprForFile->iHeadLen;
return 0;
}
else
{
return -1; /* 如果没有获得编码方式,就返回-1 */
}
}
(4)设置字体细节函数:
int SetFontsDetail(char *pcHZKFile, char *pcFileFreeTypem, unsigned int dwFontSize)
{
int iError = 0;
PT_FontOpr ptFontOpr;
PT_FontOpr ptTmp;
int iRet = -1;
g_dwFontSize = dwFontSize;
/* 从对应的编码方式结构体中取出ptFontOprSupportedHead链表,遍历链表中的每一项的,根 * 据名字来确定是哪一项,然后进行相应字体的初始化 */
ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
while (ptFontOpr)
{
if (strcmp(ptFontOpr->name, "ascii") == 0)
{
iError = ptFontOpr->FontInit(NULL, dwFontSize);
}
else if (strcmp(ptFontOpr->name, "gbk") == 0)
{
iError = ptFontOpr->FontInit(pcHZKFile, dwFontSize);
}
else
{
iError = ptFontOpr->FontInit(pcFileFreeTypem, dwFontSize);
}
DBG_PRINTF("%s %d\n", ptFontOpr->name, iError);
ptTmp = ptFontOpr->ptNext;
if (iError == 0)
{
/* 比如对于ascii编码的文件, 可能用ascii字体也可能用gbk字体,
* 所以只要有一个FontInit成功, SetTextDetail最终就返回成功
*/
iRet = 0;
}
else
{
DelFontOprFrmEncoding(g_ptEncodingOprForFile, ptFontOpr);
}
ptFontOpr = ptTmp;
}
return iRet;
}
(5)选择设置显示器函数:
int SelectAndInitDisplay(char *pcname)
{
int iError;
g_ptDispOpr = GetDispOpr(pcname);
/* 这个函数根据函数的参数pcname来获取相应的显示器,并返回一个DispOpr结构体 */
if (!g_ptDispOpr)
{
return -1;
}
iError = g_ptDispOpr->DeviceInit();
/* 调用返回的结构体里面的DeviceInit函数来进行显示器设备的初始化 */
return iError;
}
(6)X方向增加判断函数:
int IncLcdX (int iX)
{
if (iX + 1 < g_ptDispOpr->iXres)
return (iX + 1);
else
return 0;
}
这个是x方向位移增加判断函数,在显示的时候,如果下一个字符已经超出LCD的边界,那么我们不应直接显示这个字符,而应该换行显示,这个IncLcdX函数非常巧妙。如果iX + 1 < g_ptDispOpr->iXres(LCD的x方向的最大值)的话,函数返回iX + 1,如果超出LCD的x方向的最大值的话,则返回0。这个函数先判断再返回。
(7)Y方向增加判断函数:
int IncLcdY (int iY)
{
if (iY + g_dwFontSize < g_ptDispOpr->iYres)
return (iY + g_dwFontSize);
else
return 0;
}
同理,这个函数是y方向的增加判断函数,在x方向,增量为1,在y方向上面,增量就是g_dwFontSize,也就是所设置的字体大小。
(8)重新计算字体位置函数:
int RelocateFontPos(PT_FontBitMap ptFontBitMap)
{
int iLcdY;
int iDeltaX;
int iDeltaY;
/* 这个函数应该是为了防止字体设置过大,比如,字体设置为100,获取的位图Y方向的最大值已 * 经超过LCD显示器Y方向的最大值的话,就直接返回-1,不再显示
*/
if (ptFontBitMap->iYMax > g_ptDispOpr->iYres)
return -1;
/* 如果位图X方向的最大值超出LCD显示器的最大值的话,就换行显示,换行的时候需要判断Y方 * 向是否满页,用IncLcdY函数来判断显示
*/
if (ptFontBitMap->iXMax > g_ptDispOpr->iXres)
{
iLcdY = IncLcdY(ptFontBitMap->iCurOriginY);
if (iLcdY == 0) /* 说明满页了 */
{
return -1;
}
else
{
iDeltaX = 0 - ptFontBitMap->iCurOriginX;
iDeltaY = iLcdY - ptFontBitMap->iCurOriginY;
ptFontBitMap->iCurOriginX += iDeltaX;
ptFontBitMap->iCurOriginY += iDeltaY;
ptFontBitMap->iNextOriginX += iDeltaX;
ptFontBitMap->iNextOriginY += iDeltaY;
ptFontBitMap->iXLeft += iDeltaX;
ptFontBitMap->iXMax += iDeltaX;
ptFontBitMap->iYTop += iDeltaY;
ptFontBitMap->iYMax += iDeltaY;
return 0;
}
}
return 0;
}
(9)显示一个字:
int ShowOneFont(PT_FontBitMap ptFontBitMap)
{
int x;
int y;
unsigned char ucByte = 0;
int i = 0;
int bit;
if (ptFontBitMap->iBpp == 1)
{
for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
{
i = (y - ptFontBitMap->iYTop) * ptFontBitMap->iPitch;
for (x = ptFontBitMap->iXLeft, bit = 7; x < ptFontBitMap- >iXMax; x++)
{
if (bit == 7)
{
ucByte = ptFontBitMap->pucBuffer[i++];
}
if (ucByte & (1<<bit))
{
g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
}
else
{
g_ptDispOpr->ShowPixel(x, y, 0);
}
bit--;
if (bit == -1)
{
bit = 7;
}
}
}
}
/* 上面一段的解释我用一张图代替了,用图解释比较清楚 */
else if (ptFontBitMap->iBpp == 8)
{
for (y = ptFontBitMap->iYTop; y < ptFontBitMap->iYMax; y++)
for (x = ptFontBitMap->iXLeft; x < ptFontBitMap->iXMax; x++)
{
if (ptFontBitMap->pucBuffer[i++])
g_ptDispOpr->ShowPixel(x, y, COLOR_FOREGROUND);
}
}
/* 如果bpp==8的话,说明bit per pixel = 8,即1字节=1像素。每一个pucBuffer[i]即 * 代表一个像素,直接用ShowPixel来描画即可。 */
else
{
DBG_PRINTF("ShowOneFont error! cannot support %d bpp!\n", ptFontBitMap->iBpp);
return -1;
}
return 0;
}
(10)显示一页函数:
int ShowOnePage (unsigned char *pucTextFileMemCurPos)
{
int iLen;
int iError;
unsigned char *pucBufStart;
unsigned int dwCode;
PT_FontOpr ptFontOpr;
T_FontBitMap tFontBitMap;
int bHasNotClrScreen = 1;
int bHasGetCode = 0;
tFontBitMap.iCurOriginX = 0;
tFontBitMap.iCurOriginY = g_dwFontSize;
pucBufStart = pucTextFileMemCurPos; /* 圈1 */
while (1)
{
iLen = g_ptEncodingOprForFile->GetCodeFrmBuf(pucBufStart, g_pucTextFileMemEnd, &dwCode);
/* 一次GetCodeFrmBuf只能获得dwCode大小的编码,所以取一次,只能获得一个字符, * 所以这个函数用一个while(1)循环,不断地取,直到满足的条件才退出。
*/
if (iLen == 0)
{
/* 文件结束 */
if (!bHasGetCode)
{
return -1;
}
else
{
return 0;
}
}
bHasGetCode = 1;
pucBufStart += iLen;
/* 有些文本, \n\r两个一起才表示回车换行
* 碰到这种连续的\n\r, 只处理一次*/
if (dwCode == '\n')
{
g_pucLcdNextPosAtFile = pucBufStart; /* 圈2 */
/* 回车换行 */
tFontBitMap.iCurOriginX = 0;
tFontBitMap.iCurOriginY = IncLcdY(tFontBitMap.iCurOriginY);
/* 回车换行就是x不变,y增加1,y增加的话用IncLcdY这个增加判断函数 */
if (0 == tFontBitMap.iCurOriginY)
{
/* 显示完当前一屏了 */
return 0;
}
else
{
continue;
}
}
else if (dwCode == '\r')
{
continue;
}
else if (dwCode == '\t')
{
/* TAB键用一个空格代替 */
dwCode = ' ';
}
DBG_PRINTF("dwCode = 0x%x\n", dwCode);
ptFontOpr = g_ptEncodingOprForFile->ptFontOprSupportedHead;
/* 对于一种编码方式,可以用一种或几种字体来显示,这些字体就在 * g_ptEncodingOprForFile->ptFontOprSupportedHead这个链表中,从这个链表 * 中一一取出试验,来获取字体的位图。
*/
while (ptFontOpr)
{
iError = ptFontOpr->GetFontBitmap(dwCode, &tFontBitMap);
/* 这个函数成功返回0,失败返回-1 */
if (0 == iError)
{
if (RelocateFontPos(&tFontBitMap))
{
/* 剩下的LCD空间不能满足显示这个字符 */
return 0;
}
if (bHasNotClrSceen)
{
/* 首先清屏
g_ptDispOpr->CleanScreen(COLOR_BACKGROUND);
bHasNotClrSceen = 0;
}
/* 显示一个字符 */
if (ShowOneFont(&tFontBitMap))
{
return -1;
}
/* 更新下一个字符的显示位置 */
tFontBitMap.iCurOriginX = tFontBitMap.iNextOriginX;
tFontBitMap.iCurOriginY = tFontBitMap.iNextOriginY;
g_pucLcdNextPosAtFile = pucBufStart; /* 圈3 */
/* 继续取出下一个编码来显示 */
break;
}
ptFontOpr = ptFontOpr->ptNext;
}
}
return 0;
}
可以总结ShowOnePage的过程:
while(1)
{
1)GetCideFrmBuf
2)GetFontBitMap
3)重新计算位置
4)显示
}
我用红色字体显示pucBufStart在这个函数中的位置,根据这个变量的变化,我们可以屡清很多关系:
1)解释/*圈1*/:pucBufStart这个参数是指从内存中的哪个地方来取值,用于GetCodeFrmBuf()函数的第一个参数,它的初始值是ShowOnePage()函数的参数,而这个参数是从ShowNextPage()中传过来的,所以pucBufStart = g_pucLcdFirstPosAtFile = g_pucTextFileMem + g_ptEncodingOprForFile->iHeadLen(这个是在OpenTextfile中确定的)
2)解释/*圈2*/:iLen是GetCodeFrmBuf()函数的返回值,pucBufStart += iLen;
g_pucLcdNextPosAtFile这个参数是值LCD上显示的下一个字符编码在内存中的位置,注意到/*圈2*/下面有一个continue,退出本次while循环,即如果获得的编码是'\n'字符,x不变,y增加1,然后就不再显示这个字符,直接换行就可以了(回车),但是还要记录下一个字符在内存中的位置,所以有/*圈2*/语句。
3)如果程序继续执行,从内存中取编码,获取位图,显示,显示完一个字符以后,需要用/*圈3*/语句来记录下一个字符在内存中的位置。
4)注意,ShowOnePage()这个函数,其实是一个字符一个字符来处理显示的,也就是说,会经历很多次while()循环,除非文件结束或LCD不能满足显示字符的条件(即满屏了),这时候,pucBufStart参数记录的是lcd下一页第一个字符在内存中的位置,也即g_pucLcdNextPosAtFile这个参数(因为他俩相同),在ShowNextPage()函数中赋给pucLcdNextPageFirstPosAtFile这个参数。
(11)记录页面函数:
static void RecordPage(PT_PageDesc ptPageNew)
{
PT_PageDesc ptPage;
if (!g_ptPages)
{
g_ptPages = ptPageNew;
}
else
{
ptPage = g_ptPages;
while (ptPage->ptNextPage)
{
ptPage = ptPage->ptNextPage;
}
ptPage->ptNextPage = ptPageNew;
ptPage->ptPrePage = ptPage;
}
}
这个函数是链表操作的普通操作,将ptPageNew这个结构体添加到g_ptPages这个链表中,重要的是这样做的目的,是为了以后方便上下翻页。
(12)显示下一页函数:
int ShowNextPage (void)
{
int iError;
PT_PageDesc ptPage;
unsigned char *pucTextFileMemCurPos;
if (g_ptCurPage) /* g_ptCurPage初始值为NULL,第一次执行这个函数不会执行这句 */
{
pucTextFileMemCurPos = g_ptCurPage->pucLcdNextPageFirstPosAtFile;
}
else
{
pucTextFileMemCurPos = g_pucLcdFirstPosAtFile;
/* g_pucLcdFirstPosAtFile这个参数在OpenTextFile里面进行了设置 */
}
iError = ShowOnePage(pucTextFileMemCurPos);
if (iError == 0) /* success to show ont page */
{
/* 第一次调用ShowNextPage时不会执行这一步 */
if (g_ptCurPage && g_ptCurPage->ptNextPage)
{
g_ptCurPage = g_ptCurPage->ptNextPage;
return 0;
}
/* 执行这一步的目的是想执行一次ShowNextPage生成一个ptPage结构体,包含
* pucLcdFirstPosAtFile和pucLcdNextPageFirstPosAtFile等信息,同时将ptPage赋值给
* g_ptCurPage。这样再次调用ShowNextPage()函数时,就可以使用这些信息。
* 同时调用RecordPage()这个函数,将每一页都加入链表(g_ptPages)中,这样这个链表中就有每一
* 页的一些信息,比如显示了1~10页,现在返回到第3页,输入n显示下一页,函数就会执行上面if
* (g_ptCurPage)和if (g_ptCurPage && g_ptCurPage->ptNextPage)两个if语句。
*/
ptPage = malloc(sizeof(T_PageDesc));
if (ptPage)
{
/* 初始化ptPage */
ptPage->pucLcdFirstPosAtFile = pucTextFileMemCurPos;
ptPage->pucLcdNextPageFirstPosAtFile = g_pucLcdNextPosAtFile;
ptPage->ptPrePage = NULL;
ptPage->ptNextPage = NULL;
g_ptCurPage = ptPage;
RecordPage(ptPage);
return 0;
}
else
{
return -1;
}
}
return iError;
}