/* * ACF就是把bdf字体进行二进制压缩。其文件分为三个部分 * 1文件头,总共6字节,前2字节是字体数量,后面4字节是bdf的FONTBOUNDINGBOX * 2数据索引,总共是字体数量*5个字节。其中5个字节内容是前2字节的unicode编码和后3字节的数据偏移量 * 3数据,每个数据是前4个字节的bbx,第5个字节的前7位是DWIDTH的x分量,后1位0表示位图每次读取1字节,为1表示每次读取2字节。第6个字节及以后是BITMAP的数据,总长度是bbx[1]的长度。 */ #include "arm11/acf.h" #include "arm11/console.h" #include "arm11/fmt.h" #include "fs.h" // 画点函数 #ifndef ACF_LIT_POINT #error "ACF_LIT_POINT SHOULD BE DECLARE BEFORE USING GB2312 FONT" #endif extern uint8_t canvas[]; typedef struct { uint16_t start; uint16_t end; uint8_t padding; uint8_t bbxdcnt; } Fragment; typedef struct { uint8_t width; uint8_t height; int8_t offsetx; int8_t offsety; char *filename; uint32_t filesize; uint16_t fragsize; Fragment *fragments; } ACFont; static ACFont gblfont = { 0, 0, 0, 0, NULL, 0, 0, NULL }; #define ACFONT_HEAD_SIZE 10u #define ACFONT_HEAD_MAGNO 0x02464341u #define ACFONT_HEAD_WIDTH(buf) ((buf)[4]) #define ACFONT_HEAD_HEIGHT(buf) ((buf)[5]) #define ACFONT_HEAD_OFFSETX(buf) ((int8_t)(buf)[6]) #define ACFONT_HEAD_OFFSETY(buf) ((int8_t)(buf)[7]) #define ACFONT_HEAD_MEMFRAG(buf) (*(uint16_t*)( (buf)+8 )) #define ACFONT_SIZEOF_BBX 4u #define ACFONT_SIZEOF_GLYPH 64u #define ACFONT_BBXD_FROM_FRAG(pFrag, idx) (uint8_t*)( (uint8_t*)(pFrag) + sizeof(Fragment) + ACFONT_SIZEOF_BBX*(idx) ) // 提供两个方法: // 1 设置字体文件名称 // 2 使用该字体在一个原点范围内渲染一行文字 // 设置字体名称 int acf_set_font(const char *acfile) { // open the font file FHandle font; uint32_t readed; if( RES_OK != fOpen(&font, acfile, FA_OPEN_EXISTING | FA_READ) ) return ACFONT_NOT_FOUND; // 读取数据 uint8_t head[ACFONT_HEAD_SIZE]; if( RES_OK != fRead(font, head, ACFONT_HEAD_SIZE, &readed) || readed != ACFONT_HEAD_SIZE ) { fClose(font); return ACFONT_INVALID; } if ( *(uint32_t*)(head) != ACFONT_HEAD_MAGNO ) { fClose(font); return ACFONT_NOT_SUPPORT; } // assign basic data from file gblfont.width = ACFONT_HEAD_WIDTH(head); gblfont.height = ACFONT_HEAD_HEIGHT(head); gblfont.offsetx = ACFONT_HEAD_OFFSETX(head); gblfont.offsety = ACFONT_HEAD_OFFSETY(head); // get filesize gblfont.filesize = fSize(font); if (gblfont.filename != NULL) { free(gblfont.filename); gblfont.filename = NULL; } if (gblfont.fragments) { free( gblfont.fragments ); gblfont.fragments = NULL; } //if( RES_OK != fLseek(font, ACFONT_HEAD_SIZE) ) //{ // fClose( font ); // return ACFONT_INVALID; //} // get index chapters // 1 decide chapter count uint16_t memfrag = ACFONT_HEAD_MEMFRAG(head); uint8_t *fragments = (uint8_t*)malloc(memfrag); if( fragments == NULL ) { fClose(font); return ACFONT_MEM_EMPTY; } int fill = 0; do{ Result res; if( RES_OK != (res=fRead(font, fragments+fill, memfrag-fill < 256 ? memfrag-fill : 256, &readed)) ){ free( fragments ); fClose( font ); return ACFONT_INVALID; } fill += readed; } while( fill < memfrag ); // save filename for `fopen` int len = strlen(acfile); char *file = (char *)malloc(len + 1); if (file == NULL) { free( fragments ); memset(&gblfont, 0, sizeof(gblfont)); fClose(font); return ACFONT_MEM_EMPTY; } memcpy(file, acfile, len); file[len] = 0; gblfont.filename = file; gblfont.fragments = (Fragment*)fragments; gblfont.fragsize = memfrag; fClose(font); return 0; } /* | Unicode符号范围 | UTF-8编码方式 n | (十六进制) | (二进制) ---+-----------------------+------------------------------------------------------ 1 | 0000 0000 - 0000 007F | 0xxxxxxx 2 | 0000 0080 - 0000 07FF | 110xxxxx 10xxxxxx 3 | 0000 0800 - 0000 FFFF | 1110xxxx 10xxxxxx 10xxxxxx 4 | 0001 0000 - 0010 FFFF | 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx 5 | 0020 0000 - 03FF FFFF | 111110xx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 6 | 0400 0000 - 7FFF FFFF | 1111110x 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx 10xxxxxx */ #define BYTE_H 1u << 7 #define TEST_H(c) ((c)&BYTE_H) #define ISSET_H(c) TEST_H(c) == BYTE_H #define EXBYTE 0x3fu #define EXDATA(c) ((c)&EXBYTE) static inline const char *next_unicode(const char *utf8, uint32_t *code) { if (utf8 == NULL || *utf8 == '\0') return NULL; uint8_t c = *utf8++; if (c > 0x7f) { int n; for (n = 0; ISSET_H(c); c <<= 1) ++n; if (n > 1) { for (*code = 0, c >>= n; n-- > 0; c = EXDATA(*utf8++)) *code = (*code << 6) | c; --utf8; } else *code = c; } else *code = c; return utf8; } static int bsearch_font(uint32_t unicode, Fragment **pFrag) { int retval = 0; uint8_t *memory = (uint8_t*)gblfont.fragments; for( int i=0; i < gblfont.fragsize; ) { Fragment *frag = (Fragment*)&memory[i]; if( frag->start <= unicode ) { if( unicode <= frag->end ) { // hit int diff = unicode - frag->start; *pFrag = frag; return retval + frag->padding * diff; } else { retval += frag->padding * (frag->end - frag->start + 1); i += sizeof(Fragment) + ACFONT_SIZEOF_BBX * frag->bbxdcnt; } } else return ACFONT_NOT_FOUND; } return ACFONT_NOT_FOUND; } #define BIT_PER_BYTE 8 #define BIT_AT_POS(mem, idx) ((mem)[(idx) / BIT_PER_BYTE] & (1 << ((idx) % BIT_PER_BYTE))) #define SET_AT_POS(mem, idx) ((mem)[(idx) / BIT_PER_BYTE] |= (1 << ((idx) % BIT_PER_BYTE)) ) #define SIZEOF_S6 6 static inline int readS6(uint8_t *p, int index) { int ret = 0; int symbol = BIT_AT_POS(p, index); for (int i = 0; i < SIZEOF_S6 - 1; ++i) { ret <<= 1; ++index; if (BIT_AT_POS(p, index)) ret |= 1; } return symbol ? -ret : ret; } #define PIX_IN_LINE(x,y,w) (x) + (y)*(w) static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, uint32_t code, unsigned *width_max, uint8_t *ram) { uint32_t readed; // 获取code对应的字体信息 Fragment *frag; int font_pos = bsearch_font(code, &frag); if (font_pos < 0) { return -1; } font_pos += ACFONT_HEAD_SIZE + gblfont.fragsize; if ((uint32_t)font_pos > gblfont.filesize) { return -2; } uint8_t font[ACFONT_SIZEOF_GLYPH], *glyph; if( RES_OK != fLseek(fd, font_pos) ) return -3; if( RES_OK != fRead(fd, font, frag->padding, &readed) || readed != (uint32_t)frag->padding ) return -4; int8_t bbx[4], fw; uint8_t *bbxd; if( frag->bbxdcnt == 1 ) { bbxd = ACFONT_BBXD_FROM_FRAG(frag, 0); glyph = font; } else { uint8_t index = font[0]; bbxd = ACFONT_BBXD_FROM_FRAG(frag, index); glyph = font+1; } bbx[0] = readS6(bbxd, 0); bbx[1] = readS6(bbxd, SIZEOF_S6); bbx[2] = readS6(bbxd, SIZEOF_S6 * 2); bbx[3] = readS6(bbxd, SIZEOF_S6 * 3); fw = readS6(bbxd, SIZEOF_S6 * 4); // render // 从上往下(y从大到小,x从小到大)进行绘制 int px = *x, py = 0-gblfont.offsety; // px/py不参与计算位置 int cx = px + bbx[2], cy = py + bbx[1] + bbx[3]; // cx/cy计算位置进行绘制 // 先检查宽度 if ((width_max != NULL) && (cx + bbx[0] >= (int)(*width_max))) return 1; // 绘制 for (int i = 0; i < bbx[1]; ++i) { for (int j = 0; j < bbx[0]; ++j) { if( 0 <= i && i < (int)height && 0 <= j && j < (int)width ) { if( BIT_AT_POS(glyph, bbx[0] * i + j) ) { SET_AT_POS( ram, PIX_IN_LINE(cx+j, cy-i, width) ); } } } } // 更新 *x = px + fw; return 0; } // 根据字体绘制中文字符 // x,y - 绘制一行字符的基准点 // maxwidth - 最长绘制多少个像素点,填0则忽略此参数 // utf8_line - utf8字符串 // 返回:第一个未绘制的字符的位置,如果width为0,则返回永远是NULL const char *acf_draw(int x, int y, unsigned width, unsigned height, unsigned maxwidth, const char *utf8_line) { FHandle font; int linex = 0; if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_OPEN_EXISTING | FA_READ) ) { // log return utf8_line; } uint32_t unicode; if( maxwidth == 0 ) maxwidth = width; unsigned *option = width == 0 ? NULL : &maxwidth; const int ramsize = gblfont.height * maxwidth + BIT_PER_BYTE - 1 / BIT_PER_BYTE; uint8_t *localram = malloc( ramsize ); memset( localram, 0, ramsize ); for (const char *next = next_unicode(utf8_line, &unicode); next != NULL; next = next_unicode(utf8_line, &unicode)) { int error = render_unicode(font, &linex, maxwidth, height, unicode, option, localram); if (error) { free( localram ); fClose(font); return utf8_line; } utf8_line = next; } // copy back to canvas uint16_t *frame = consoleGet()->frameBuffer; uint16_t fg = consoleGetFgColor(); for( int i=0; i < gblfont.height; ++i ) { for( int j=0; j < (int)maxwidth; ++j ) { if( BIT_AT_POS(localram, PIX_IN_LINE(j, i, maxwidth)) ) { ACF_LIT_POINT(x+j, y-(gblfont.height-1-i), (int)width, (int)height, 1); } } } free( localram ); fClose(font); return NULL; }