本文用汉字库点阵和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)
下面给出源码,也可到这里直接下面: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;
}
电子书实现效果: