/* * 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 "fs.h" typedef struct { uint16_t start; uint16_t end; uint8_t padding; uint8_t bbxdcnt; } Fragment; typedef struct { acf_rectedge_t width; acf_rectedge_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 使用该字体在一个原点范围内渲染一行文字 // 设置字体名称 acf_error_t acf_initialize(acf_text_t 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, 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 = -1-gblfont.offsety; // px/py不参与计算位置 int cx = px + bbx[2], cy = py + bbx[1] + bbx[3]; // cx/cy计算位置进行绘制 // 先检查宽度 if ( cx + bbx[0] >= (int)width ) return 1; // 绘制 if ( ram != NULL ) { for (int i = 0; i < bbx[1]; ++i) { for (int j = 0; j < bbx[0]; ++j) { int tx = cx+j, ty = cy-i; // see: https://www.zhihu.com/question/264505093/answer/281849883 if( ( tx | ((int)width-tx) | ty | ((int)height-ty) ) > 0 ) // 0 <= tx && tx < width && 0 <= ty && ty <= height { if( BIT_AT_POS(glyph, bbx[0] * i + j) ) { SET_AT_POS( ram, PIX_IN_LINE(tx, ty, width) ); } } } } } // 更新 *x = px + fw; return 0; } #define ACFONT_CANVAS_MEMSIZE( w, h ) ( ( (w)*(h) + BIT_PER_BYTE-1 )/BIT_PER_BYTE+2*sizeof(acf_rectedge_t) ) #define ACFONT_CANVAS_WIDTH( p ) (*(acf_rectedge_t*)(p)) #define ACFONT_CANVAS_RWIDTH( p ) (*((acf_rectedge_t*)(p)+1)) #define ACFONT_CANVAS_PIXEL( p ) ((uint8_t*)(p)+2*sizeof(acf_rectedge_t)) void acf_recycle( acf_canvas_t canvas ) { if( canvas != NULL ) free(canvas); } acf_canvas_t acf_use_canvas(acf_canvas_t canvas, acf_boolean_t use_rwidth, acf_visitor_t pixel_responser, acf_callerdata_t data) { if( canvas == NULL ) return canvas; register acf_rectedge_t width = ACFONT_CANVAS_WIDTH(canvas), height = gblfont.height; register acf_rectedge_t loop_width = use_rwidth ? ACFONT_CANVAS_RWIDTH(canvas) : width; for( acf_position_t i=0; i < height; i++ ) { acf_position_t dy = height - 1 - i; for( register acf_position_t j=0; j < loop_width; j++ ){ uint8_t m = BIT_AT_POS(ACFONT_CANVAS_PIXEL(canvas), PIX_IN_LINE(j, dy, width)); pixel_responser(data, j, i, m>0 ? 1 : 0); } } return canvas; } // 根据字体绘制中文字符 // x,y - 绘制一行字符的基准点 // maxwidth - 最长绘制多少个像素点,填0则忽略此参数 // utf8_line - utf8字符串 // 返回:第一个未绘制的字符的位置,如果width为0,则返回永远是NULL acf_canvas_t acf_get_canvas(acf_rectedge_t width, acf_text_t text, acf_rectedge_t *realwidth, acf_counter_t *renderedcnt, acf_text_t *rest) { FHandle font; if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_OPEN_EXISTING | FA_READ) ) { // log return NULL; } uint32_t unicode; int linex = 0; int rendered_count = 0; const char *utf8_line = text; acf_rectedge_t *canvas = malloc( ACFONT_CANVAS_MEMSIZE(width, gblfont.height) ); memset(canvas, 0, ACFONT_CANVAS_MEMSIZE(width, gblfont.height)); for (const char *next = next_unicode(utf8_line, &unicode); next != NULL; next = next_unicode(utf8_line, &unicode)) { int error = render_unicode(font, &linex, width, gblfont.height, unicode, ACFONT_CANVAS_PIXEL(canvas)); if( error > 0 ) break; else if( error < 0 ) error = render_unicode(font, &linex, width, gblfont.height, '?', ACFONT_CANVAS_PIXEL(canvas)); if( error ) { free( canvas ); fClose(font); return NULL; } utf8_line = next; ++rendered_count; } if( realwidth != NULL ) *realwidth = linex; if( renderedcnt != NULL ) *renderedcnt = rendered_count; if( rest != NULL ) *rest = utf8_line; *canvas = width; *(canvas+1) = linex; fClose(font); return canvas; } acf_error_t acf_calculate(acf_rectedge_t width, acf_text_t text, acf_rectedge_t *realwidth, acf_counter_t *renderedcnt, acf_text_t *rest) { FHandle font; if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_OPEN_EXISTING | FA_READ) ) { // log return ACFONT_NOT_FOUND; } uint32_t unicode; int linex = 0; int rendered_count = 0; const char *utf8_line = text; for (const char *next = next_unicode(utf8_line, &unicode); next != NULL; next = next_unicode(utf8_line, &unicode)) { int error = render_unicode(font, &linex, width, gblfont.height, unicode, NULL); if( error > 0 ) break; else if( error < 0 ) error = render_unicode(font, &linex, width, gblfont.height, '?', NULL); if( error ) { fClose(font); return error; } utf8_line = next; ++rendered_count; } if( realwidth != NULL ) *realwidth = linex; if( renderedcnt != NULL ) *renderedcnt = rendered_count; if( rest != NULL ) *rest = utf8_line; fClose(font); return 0; }