HZK16實現電子書

        本文用漢字庫點陣和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;
}


電子書實現效果:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章