本文用漢字庫點陣和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;
}
電子書實現效果: