/* * 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" // 画点函数 #ifndef ACF_LIT_POINT #error "ACF_LIT_POINT SHOULD BE DECLARE BEFORE USING GB2312 FONT" #endif typedef struct { uint8_t width; uint8_t height; int8_t offsetx; int8_t offsety; uint16_t amount; char *filename; uint16_t *chapter; uint32_t filesize; } ACFont; static ACFont gblfont = { 0, 0, 0, 0, 0, NULL, NULL, 0}; #define ACFONT_HEAD_SIZE 6 #define ACFONT_HEAD_AMOUNT(buf) ((buf)[0] + (buf)[1] * 0x100) #define ACFONT_HEAD_WIDTH(buf) ((buf)[2]) #define ACFONT_HEAD_HEIGHT(buf) ((buf)[3]) #define ACFONT_HEAD_OFFSETX(buf) ((int8_t)(buf)[4]) #define ACFONT_HEAD_OFFSETY(buf) ((int8_t)(buf)[5]) #define ACFONT_SIZEOF_INDEX 5 #define ACFONT_INDEX(n) (ACFONT_HEAD_SIZE + (n)*ACFONT_SIZEOF_INDEX) #define ACFONT_INDEX_VALUE(data) (0x10000 * data[2] + data[3] + data[4] * 0x100) #define ACFONT_SIZEOF_BBX 4 #define ACFONT_SIZEOF_GLYPH 64 #define ACFONT_CHAPTER_MEMORY 480 // 提供两个方法: // 1 设置字体文件名称 // 2 使用该字体在一个原点范围内渲染一行文字 // 设置字体名称 int acf_set_font(const char *acfile) { // open the font file FHandle font; u32 readed; if( RES_OK != fOpen(&font, acfile, 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 (ACFONT_SIZEOF_BBX + ACFONT_HEAD_HEIGHT(head) * ACFONT_HEAD_WIDTH(head) / 8 > ACFONT_SIZEOF_GLYPH) { fClose(font); return ACFONT_NOT_SUPPORT; } // assign basic data from file gblfont.amount = ACFONT_HEAD_AMOUNT(head); 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); if (gblfont.chapter != NULL) free(gblfont.chapter); // get index chapters // 1 decide chapter count const int nparts = ACFONT_CHAPTER_MEMORY / ACFONT_SIZEOF_INDEX; int nchapter = gblfont.amount / nparts; if (nchapter * nparts < gblfont.amount) { ++nchapter; } // 2 alloc cache for chapter uint16_t *pchapter = (uint16_t *)malloc(nchapter * sizeof(uint16_t)); if (pchapter == NULL) { memset(&gblfont, 0, sizeof(gblfont)); fClose(font); return ACFONT_MEM_EMPTY; } // 3 load chapter to cache for (int i = 0; i < nchapter; ++i) { int pos = ACFONT_INDEX(i * nparts); if( RES_OK != fLseek(font, pos) ) { free(pchapter); fClose(font); return ACFONT_INVALID; } if( RES_OK != fRead(font, head, 2, &readed) || readed != 2 ) { free(pchapter); fClose(font); return ACFONT_READ_EOF; } pchapter[i] = head[0] | (head[1] << 8); } gblfont.chapter = pchapter; // save filename for `fopen` int len = strlen(acfile); char *file = (char *)malloc(len + 1); if (file == NULL) { free(gblfont.chapter); memset(&gblfont, 0, sizeof(gblfont)); fClose(font); return ACFONT_MEM_EMPTY; } memcpy(file, acfile, len); file[len] = 0; gblfont.filename = file; 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(FHandle fd, uint32_t unicode) { u32 readed; // decide chapter count const int nparts = ACFONT_CHAPTER_MEMORY / ACFONT_SIZEOF_INDEX; int nchapter = gblfont.amount / nparts; if (nchapter * nparts < gblfont.amount) { ++nchapter; } int min = 0, max = nchapter; int m, found = 0; // search in chapter for (m = (min + max) >> 1; min < m; m = (min + max) >> 1) { uint16_t code = gblfont.chapter[m]; if (unicode < code) max = m; else if (code < unicode) min = m; else { found = 1; break; } } if (found) { uint8_t v[ACFONT_SIZEOF_INDEX]; if( RES_OK != fLseek(fd, ACFONT_INDEX(nparts * m)) ) return ACFONT_INVALID; if( RES_OK != fRead(fd, v, ACFONT_SIZEOF_INDEX, &readed) || readed != ACFONT_SIZEOF_INDEX ) return ACFONT_READ_EOF; return ACFONT_INDEX_VALUE(v); } // not match in chapter, search in chapter's parts int len = m + 1 == nchapter ? (gblfont.amount % nparts) : nparts; int size = len * ACFONT_SIZEOF_INDEX; if( RES_OK != fLseek( fd, ACFONT_INDEX( nparts * m) ) ) return ACFONT_INVALID; uint8_t *cache = (uint8_t *)malloc(size); if( RES_OK != fRead(fd, cache, size, &readed) || readed != (u32)size ) { free(cache); return ACFONT_READ_EOF; } for (min = 0, max = len, m = len >> 1; min < m; m = (min + max) >> 1) { int idx = m * ACFONT_SIZEOF_INDEX; uint16_t code = cache[idx] | (cache[idx + 1] << 8); if (unicode < code) max = m; else if (code < unicode) min = m; else { uint8_t *index = &cache[idx]; found = ACFONT_INDEX_VALUE(index); break; } } free(cache); return found ? found : -1; } #define BIT_AT_POS(mem, idx) ((mem)[(idx) / 8] & (1 << ((idx) % 8))) #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; } static int render_unicode(FHandle fd, int *x, int *y, unsigned width, unsigned height, uint32_t code, unsigned *width_max) { u32 readed; u16 color = consoleGetFgColor(); u16 *frame = consoleGetDefault()->frameBuffer; // 获取code对应的字体信息 int font_pos = bsearch_font(fd, code); if (font_pos < 0) return 0; if ((uint32_t)font_pos > gblfont.filesize) return 0; font_pos += ACFONT_HEAD_SIZE + gblfont.amount * ACFONT_SIZEOF_INDEX; uint8_t font[ACFONT_SIZEOF_GLYPH]; if( RES_OK != fLseek(fd, font_pos) ) return 1; if( RES_OK != fRead(fd, font, ACFONT_SIZEOF_GLYPH, &readed) || (readed != ACFONT_SIZEOF_GLYPH && (uint32_t)font_pos + ACFONT_SIZEOF_GLYPH < gblfont.filesize) ) return 1; int8_t bbx[4]; bbx[0] = readS6(font, 0); bbx[1] = readS6(font, SIZEOF_S6); bbx[2] = readS6(font, SIZEOF_S6 * 2); bbx[3] = readS6(font, SIZEOF_S6 * 3); int fw = readS6(font, SIZEOF_S6 * 4); // render // 从上往下(y从大到小,x从小到大)进行绘制 int px = *x, py = *y; // 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) { ACF_LIT_POINT(cx + j, cy - i, (int)width, (int)height, BIT_AT_POS(font + 4, bbx[0] * i + j)); } } // 更新 *x = px + fw; *y = py; return 0; } // 根据字体绘制中文字符 // x,y - 绘制一行字符的基准点 // width - 最长绘制多少个像素点,填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) { u32 font; if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_READ) ) { // log return utf8_line; } uint32_t unicode; unsigned *option = width == 0 ? NULL : &maxwidth; for (const char *next = next_unicode(utf8_line, &unicode); next != NULL; next = next_unicode(utf8_line, &unicode)) { int error = render_unicode(font, &x, &y, width, height, unicode, option); if (error) { fClose(font); return utf8_line; } utf8_line = next; } fClose(font); return NULL; }