本文用汉字库点阵和ASCII点阵实现电子书,汉字库点阵来自汉字库文件HZK16,ASCII字符点阵来自ASCII字符点阵数组fontdata_8x16,实际上ASCII字符点阵也可以从HZK16文件中获得,本文并没有这样实现,那样可能会好点,有兴趣的朋友可以那样去实现它。
这个电子书只是对我上一篇文章《freetype实现电子书》的代码做了些修改。不一样的地方是:现在要打开的电子书是ANSI格式的电子书,从ANSI文件中获得字符编码,然后根据编码从HZK16中获得汉字点阵或从fontdata_8x6中获得ASCII字符点阵。
ANSI文件没有文件头部,一开始就是字符编码,ASCII字符编码在ANSI文件中用 1 字节存储,汉字在ANSI文件中
是GBK码,用两个字节存储编码。
ANSI文件中GBK(2字节)的合成;
code = pucBuf[0] + (((unsigned int)pucBuf[1])<<8);
GBK码含区码和段码,区码和段码的提取如下(unsigned int code为某汉字的GBK码):
unsigned int area = (code&0xFF) - 0xA1; //区码
unsigned int where = ((code&0xFF00)>>8) - 0xA1; //段码
unsigned char *dots = hzkmem + (area * 94 + where)*32; //每一区含94个字符,每个字符的点阵占32字节,
//因为点阵为16*16,即2*8(bit) * 16 = 2(byte) * 16 = 32(byte)
我写代码时犯了个错误,调试了不少时间,我那个错误就是对未初始化的指针操作了。
如:
int *p;
int b = 0;
*p = b; //编译没提示,运行就segmentfault了,原因:p没初始化,不知指向哪里。
可以这样初始化:
p = (int *)malloc(sizeof(int));
这个电子书只是对我上一篇文章《freetype实现电子书》的代码做了些修改。不一样的地方是:现在要打开的电子书是ANSI格式的电子书,从ANSI文件中获得字符编码,然后根据编码从HZK16中获得汉字点阵或从fontdata_8x6中获得ASCII字符点阵。
ANSI文件没有文件头部,一开始就是字符编码,ASCII字符编码在ANSI文件中用 1 字节存储,汉字在ANSI文件中
是GBK码,用两个字节存储编码。
ANSI文件中GBK(2字节)的合成;
code = pucBuf[0] + (((unsigned int)pucBuf[1])<<8);
GBK码含区码和段码,区码和段码的提取如下(unsigned int code为某汉字的GBK码):
unsigned int area = (code&0xFF) - 0xA1; //区码
unsigned int where = ((code&0xFF00)>>8) - 0xA1; //段码
unsigned char *dots = hzkmem + (area * 94 + where)*32; //每一区含94个字符,每个字符的点阵占32字节,
//因为点阵为16*16,即2*8(bit) * 16 = 2(byte) * 16 = 32(byte)
我写代码时犯了个错误,调试了不少时间,我那个错误就是对未初始化的指针操作了。
如:
int *p;
int b = 0;
*p = b; //编译没提示,运行就segmentfault了,原因:p没初始化,不知指向哪里。
可以这样初始化:
p = (int *)malloc(sizeof(int));
下面给出源码,也可到这里直接下面:https://download.csdn.net/download/qq_22863733/10402658
function.h:
#include <stdio.h> #include <string.h> #include <math.h> #include <sys/mman.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <linux/fb.h> #include <fcntl.h> #include <wchar.h> #include <ft2build.h> #include FT_FREETYPE_H #define WIDTH 80 #define HEIGHT 80 #define FONTSIZE 16 #define iHeadLen 0 #define DBG_PRINTF(...) #define HZK_FILE_NAME "HZK16" typedef struct PageInfo{ unsigned char * pucPagePos; struct PageInfo *pt_PrePageInfo; struct PageInfo *pt_NextPageInfo; }T_PageInfo,*PT_PageInfo; int lcd_init(void); void lcd_put_pixel(int x, int y, unsigned int color); int OpenTextFile(char *pcFileName); //unsigned int GetCodeFrmUTF8Buf(unsigned char * pucUTF8Buf); int show_one_font(int *x,int *y,unsigned int code); unsigned char * show_line(int * y,unsigned char *pucLineFirsPostAtFile); unsigned char* show_one_page(PT_PageInfo ptCurPageInfo); static void RecordPage(PT_PageInfo ptPageNew); unsigned char* Renew_pucLcdFirstPosAtFile(unsigned int code,unsigned char * pucCurPosAtFile); unsigned int AsciiGetCodeFrmBuf(unsigned char *pucCurPos); void lcd_put_chinese(int x, int y, unsigned int code); void lcd_put_ascii(int x, int y, unsigned int c); int hzk_init(void); 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; int *lcdx,*lcdy; int fd_hzk16; struct stat hzk_stat;//文件stat结构体 unsigned char *hzkmem;//把文件映射到hzkmem /* FT_Library library; FT_Face face; FT_GlyphSlot slot; FT_Matrix matrix; FT_Vector pen; FT_Error error; double angle; int target_height; int n, num_chars; */ int g_iFdTextFile; unsigned char *g_pucTextFileMem; unsigned char *g_pucLcdFirstPosAtFile; unsigned char *g_pucTextFileMemEnd; char *TextFileName; T_PageInfo g_tPageInfoHeader; PT_PageInfo g_ptPageInfoCur; PT_PageInfo g_ptPages; #define FONTDATAMAX 4096 static const unsigned char fontdata_8x16[FONTDATAMAX] = {...}
fontdata_8x16的实现太长了,4千多行,我这就省略了,需要的话自行到网上搜。
function.c:
#include"function.h" int lcd_init(void) { 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; } } /* 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;//8位像素的话,颜色值直接赋给这个内存中即可。 break; } case 16: { /* color : 0x00RRGGBB ,unsigned int 32位color的格式*/ /* 565 */ red = (color >> 16) & 0xff;//右移16位后剩下高16位,再&0xff,又剩下低8位。即取出32位color的16-23位 green = (color >> 8) & 0xff;//取出32color的8-15位 blue = (color >> 0) & 0xff;//取出32color的0-7位 color = ((red >> 3) << 11) | ((green >> 2) << 5) | (blue >> 3);//取出对应的组成565 *pen_16 = color; break; } case 32: { *pen_32 = color;//32位像素也直接写即可。 break; } default: { printf("can't surport %dbpp\n", var.bits_per_pixel); break; } } } 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_tPageInfoHeader.pucPagePos = g_pucTextFileMem + iHeadLen; return 0; } // Show a font for a Unicode.And will renew lcdx after show one font int show_one_font(int *x,int *y,unsigned int code) { if ((code < (unsigned char)0x80)) { lcd_put_ascii( *x, *y, code); *x=*x+8; } if ((code >= (unsigned char)0x80)) { lcd_put_chinese( *x, *y, code); *x=*x+16; } return 0; } //will renew lcdx and lcdy after show one line. unsigned char * show_line(int * y,unsigned char *pucLineFirsPostAtFile) { unsigned int code = AsciiGetCodeFrmBuf(pucLineFirsPostAtFile); while(((*lcdx + FONTSIZE) <= var.xres) &&(code != '\0')) { if (code == '\n') { *y = *y+18; *lcdx = 0; pucLineFirsPostAtFile=Renew_pucLcdFirstPosAtFile(code,pucLineFirsPostAtFile); code = AsciiGetCodeFrmBuf(pucLineFirsPostAtFile); return pucLineFirsPostAtFile; } else if (code == '\r') { pucLineFirsPostAtFile=Renew_pucLcdFirstPosAtFile(code,pucLineFirsPostAtFile); code = AsciiGetCodeFrmBuf(pucLineFirsPostAtFile); return pucLineFirsPostAtFile; } else if (code == '\t') { /* TAB键用一个空格代替 */ code = ' '; } show_one_font(lcdx,y,code); pucLineFirsPostAtFile=Renew_pucLcdFirstPosAtFile(code,pucLineFirsPostAtFile); code = AsciiGetCodeFrmBuf(pucLineFirsPostAtFile); } if((*lcdx + 16) > var.xres || (code == '\0')) { *lcdx=0; *y = *y + 18; } return pucLineFirsPostAtFile; } unsigned char* show_one_page(PT_PageInfo ptCurPageInfo) { PT_PageInfo ptPageToRecord = malloc(sizeof(T_PageInfo)); unsigned char * puctmp=ptCurPageInfo->pucPagePos; while(*lcdy<=(var.yres-18)) { puctmp = show_line(lcdy,puctmp); } if(ptCurPageInfo->pt_NextPageInfo == NULL) { ptPageToRecord->pucPagePos=puctmp; RecordPage(ptPageToRecord); } *lcdy=0; return puctmp; } static void RecordPage(PT_PageInfo ptPageNew) { PT_PageInfo ptPage; if (!g_ptPages) { g_ptPages = ptPageNew; } else { ptPage = g_ptPages; while (ptPage->pt_NextPageInfo) { ptPage = ptPage->pt_NextPageInfo; } ptPage->pt_NextPageInfo = ptPageNew; ptPageNew->pt_PrePageInfo = ptPage; ptPageNew->pt_NextPageInfo = NULL; } } unsigned char* Renew_pucLcdFirstPosAtFile(unsigned int code,unsigned char *pucCurPosAtFile) { int pitch; if( 0<=code &&code<=127 ) { pitch = 1; } else if( 128<=code) { pitch = 2; } /* else if( 2048<=code &&code<=65535 ) { pitch = 3; } else if( 65536<=code &&code<=1114111 ) { pitch = 4; } */ else { fprintf(stderr,"renew err\n"); return pucCurPosAtFile; } pucCurPosAtFile=&pucCurPosAtFile[pitch]; return pucCurPosAtFile; } unsigned int AsciiGetCodeFrmBuf(unsigned char *pucCurPos) { unsigned char *pucBuf = pucCurPos; unsigned char c = *pucBuf; unsigned int pdwCode; if ((pucBuf < g_pucTextFileMemEnd) && (c < (unsigned char)0x80)) { /* 返回ASCII码 */ pdwCode = (unsigned int)c; return pdwCode; } if (((pucBuf + 1) < g_pucTextFileMemEnd) && (c >= (unsigned char)0x80)) //这里可以看出ANSI文件中GBK码的构成方式 { /* 返回GBK码 */ pdwCode = pucBuf[0] + (((unsigned int)pucBuf[1])<<8); return pdwCode; } if (pucBuf < g_pucTextFileMemEnd) { /* 可能文件有损坏, 但是还是返回一个码, 即使它是错误的 */ pdwCode = (unsigned int)c; return 0; } else { /* 文件处理完毕 */ return 0; } } void lcd_put_ascii(int x, int y, unsigned int c) { unsigned char *dots = (unsigned char *)&fontdata_8x16[c*16];//从fontdata_8x16[FONTDATAMAX]数组中获得点阵起始位置 int i, b; unsigned char byte; for (i = 0; i < 16; i++)//点阵有16行 { byte = dots[i]; for (b = 7; b >= 0; b--)//点阵有8列 { if (byte & (1<<b))//判断点阵中的各个点是否为1 { /* show */ lcd_put_pixel(x+7-b, y+i, 0xffffff); /* 白 */ } else { /* hide */ lcd_put_pixel(x+7-b, y+i, 0); /* 黑 */ } } } } void lcd_put_chinese(int x, int y, unsigned int code) { //unsigned int area = str[0] - 0xA1; //unsigned int where = str[1] - 0xA1; unsigned int area = (code&0xFF) - 0xA1; unsigned int where = ((code&0xFF00)>>8) - 0xA1; unsigned char *dots = hzkmem + (area * 94 + where)*32; unsigned char byte; int i, j, b; for (i = 0; i < 16; i++)//16行 for (j = 0; j < 2; j++) { byte = dots[i*2 + j]; for (b = 7; b >=0; b--) { if (byte & (1<<b)) { /* show */ lcd_put_pixel(x+j*8+7-b, y+i, 0xffffff); /* 白 */ } else { /* hide */ lcd_put_pixel(x+j*8+7-b, y+i, 0); /* 黑 */ } } } } int hzk_init(void) { fd_hzk16 = open(HZK_FILE_NAME, O_RDONLY); if (fd_hzk16 < 0) { printf("can't open "HZK_FILE_NAME"\n"); return -1; } if(fstat(fd_hzk16, &hzk_stat)) { printf("can't get fstat\n"); return -1; } hzkmem = (unsigned char *)mmap(NULL , hzk_stat.st_size, PROT_READ, MAP_SHARED, fd_hzk16, 0); if (hzkmem == (unsigned char *)-1) { printf("can't mmap for hzk16\n"); return -1; } return 0; }
可以把这个function.c与我freetypes实现电子书的function.c对比一下,主要改了AsciiGetCodeFrmBuf()函数,和更新坐标的函数。
main.c:
基本和freetype实现电子书时一样,只是去掉freetype部分,添加hzk_init()函数,所以使用时要提供HZK16文件,没有这个文件的话,自行搜索下载即可。
#include "function.h" int main(int argc,char** argv) { char cOpr; if ( argc != 2 ) { fprintf ( stderr, "usage: %s TextFileName\n", argv[0] ); exit( 1 ); } lcdx=(int *)malloc(sizeof(int)); lcdy=(int *)malloc(sizeof(int)); lcd_init(); memset(fbmem, 0, screen_size); hzk_init(); TextFileName = argv[1]; OpenTextFile(TextFileName); g_ptPageInfoCur = &g_tPageInfoHeader; g_ptPages = &g_tPageInfoHeader; show_one_page(g_ptPageInfoCur); while (1) { printf("Enter 'n' to show next page, 'u' to show previous page, 'q' to exit: "); do { cOpr = getchar(); } while ((cOpr != 'n') && (cOpr != 'u') && (cOpr != 'q')); if (cOpr == 'n') { if(g_ptPageInfoCur->pt_NextPageInfo->pucPagePos==g_pucTextFileMemEnd) { printf("is the end!\n"); continue; } memset(fbmem, 0, screen_size); show_one_page(g_ptPageInfoCur->pt_NextPageInfo); g_ptPageInfoCur=g_ptPageInfoCur->pt_NextPageInfo; } else if (cOpr == 'u') { if(g_ptPageInfoCur->pt_PrePageInfo == NULL) { printf("this is first page!\n"); } else { memset(fbmem, 0, screen_size); show_one_page((g_ptPageInfoCur->pt_PrePageInfo)); g_ptPageInfoCur=g_ptPageInfoCur->pt_PrePageInfo; } } else { break; } } close(fd_fb); return 0; }
电子书实现效果: