From 1d3092b6f7d65b3374330a85ab415ba1a5557eb1 Mon Sep 17 00:00:00 2001 From: anod <182859762@qq.com> Date: Fri, 5 Aug 2022 17:25:19 +0800 Subject: [PATCH] show gb2312 in folder --- include/arm11/acf.h | 74 ++++++++ include/arm11/acf_cfg.h | 7 + include/arm11/acf_dev.h | 7 + source/arm11/acf.c | 370 +++++++++++++++++++++++++++++++++++++ source/arm11/filebrowser.c | 40 ++-- 5 files changed, 486 insertions(+), 12 deletions(-) create mode 100644 include/arm11/acf.h create mode 100644 include/arm11/acf_cfg.h create mode 100644 include/arm11/acf_dev.h create mode 100644 source/arm11/acf.c diff --git a/include/arm11/acf.h b/include/arm11/acf.h new file mode 100644 index 0000000..77331b0 --- /dev/null +++ b/include/arm11/acf.h @@ -0,0 +1,74 @@ +#ifndef _ANOD_COMPILED_FONT_H_ +#define _ANOD_COMPILED_FONT_H_ + +#include + +#define ACFONT_NOT_FOUND -1 +#define ACFONT_INVALID -2 +#define ACFONT_READ_EOF -3 +#define ACFONT_NOT_SUPPORT -4 +#define ACFONT_MEM_EMPTY -5 + +extern int acf_set_font(const char *); +extern const char *acf_draw(int, int, unsigned, unsigned, unsigned, const char *); + +#include "acf_dev.h" +#include "acf_cfg.h" + +#if ACF_CANVAS == ACFDEV_U8GBITMAP + +#include "c_stdint.h" +#include "c_stdio.h" +#include "c_stdlib.h" +#include "c_string.h" + +extern uint8_t *acfCanvas; + +#include "vfs.h" + +#define ACF_BYTE_SIZE 8 +#define ACF_CANVAS_SIZE(w, h) ((w) * (h) / ACF_BYTE_SIZE) +#define ACF_BYTE_WIDTH(w, h) ((w) / ACF_BYTE_SIZE) +#define ACF_BYTE_OFFSET(x, y, w, h) (((h) - (y)-1) * ACF_BYTE_WIDTH(w, h) + (x) / ACF_BYTE_SIZE) +#define ACF_U8G_BMP_BIT(x, y) (1 << (7 - (x) % 8)) + +#define FILE_T int +#define FILE_OPEN(path, mode) vfs_open(path, mode) +#define FILE_READ(f, pos, size) vfs_read(f, pos, size) +#define FILE_SEEK(f,p) vfs_lseek(f,p,VFS_SEEK_SET) +#define FILE_SEEKEND(f) vfs_lseek(f,0,VFS_SEEK_END) +#define FILE_TELL(f) vfs_tell(f) + +#define ACF_LIT_POINT(x, y, w, h, islit) \ + { \ + if (0 <= (x) && (x) < w && 0 <= (y) && (y) < h) \ + { \ + if ((islit)) \ + acfCanvas[ACF_BYTE_OFFSET(x, y, w, h)] |= ACF_U8G_BMP_BIT(x, y); \ + else \ + acfCanvas[ACF_BYTE_OFFSET(x, y, w, h)] &= ~ACF_U8G_BMP_BIT(x, y); \ + } \ + } +#elif ACF_CANVAS == ACFDEV_OPENAGB + +#include +#include +#include +#include +#include + +#include "fs.h" + +#define ACF_LIT_POINT(x, y, w, h, islit) \ + { \ + if (0 <= (x) && (x) < w && 0 <= (y) && (y) < h) \ + { \ + if ((islit)) \ + frame[240*y + x] |= color; \ + } \ + } + +#else +#endif + +#endif //_ANOD_COMPILED_FONT_H_ \ No newline at end of file diff --git a/include/arm11/acf_cfg.h b/include/arm11/acf_cfg.h new file mode 100644 index 0000000..38b48da --- /dev/null +++ b/include/arm11/acf_cfg.h @@ -0,0 +1,7 @@ +#ifndef _ANOD_COMPILED_FONT_CFG_ +#define _ANOD_COMPILED_FONT_CFG_ + +#include "acf_dev.h" +#define ACF_CANVAS ACFDEV_OPENAGB + +#endif//_ANOD_COMPILED_FONT_CFG_ \ No newline at end of file diff --git a/include/arm11/acf_dev.h b/include/arm11/acf_dev.h new file mode 100644 index 0000000..88414d3 --- /dev/null +++ b/include/arm11/acf_dev.h @@ -0,0 +1,7 @@ +#ifndef _ANOD_COMPILED_FONT_DEVICE_H_ +#define _ANOD_COMPILED_FONT_DEVICE_H_ + +#define ACFDEV_U8GBITMAP 0x00000001 +#define ACFDEV_OPENAGB 0x00000002 + +#endif//_ANOD_COMPILED_FONT_DEVICE_H_ \ No newline at end of file diff --git a/source/arm11/acf.c b/source/arm11/acf.c new file mode 100644 index 0000000..c9ac93b --- /dev/null +++ b/source/arm11/acf.c @@ -0,0 +1,370 @@ +/* + * 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 "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 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)) != RES_OK ) + 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) +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 != 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 +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 (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 && 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] >= *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, width, 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( RES_OK != fOpen(&font, gblfont.filename, FA_READ) ) + { + // log + return utf8_line; + } + + uint32_t unicode; + unsigned *option = width == 0 ? NULL : &width; + 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; +} \ No newline at end of file diff --git a/source/arm11/filebrowser.c b/source/arm11/filebrowser.c index dc497cb..d931280 100644 --- a/source/arm11/filebrowser.c +++ b/source/arm11/filebrowser.c @@ -25,15 +25,22 @@ #include "arm11/drivers/hid.h" #include "arm11/fmt.h" #include "drivers/gfx.h" +#include "acf.h" +#define screenClean() ee_printf("\x1b[2J\x1b[0m") // Notes on these settings: // MAX_ENT_BUF_SIZE should be big enough to hold the average file/dir name length * MAX_DIR_ENTRIES. #define MAX_ENT_BUF_SIZE (1024u * 196) // 196 KiB. #define MAX_DIR_ENTRIES (1000u) #define DIR_READ_BLOCKS (10u) -#define SCREEN_COLS (53u - 1) // - 1 because the console inserts a newline after the last line otherwise. -#define SCREEN_ROWS (24u) + +//#define SCREEN_COLS (53u - 1) // - 1 because the console inserts a newline after the last line otherwise. +//#define SCREEN_ROWS (24u) +#define SCREEN_COLS 19 +#define SCREEN_ROWS 14u + +#define LINENO_TO_Y(y) 4+(y)*16 #define ENT_TYPE_FILE (0) #define ENT_TYPE_DIR (1) @@ -46,8 +53,6 @@ typedef struct char *ptrs[MAX_DIR_ENTRIES]; // For fast sorting. } DirList; - - int dlistCompare(const void *a, const void *b) { const char *entA = *(char**)a; @@ -123,15 +128,17 @@ scanEnd: static void showDirList(const DirList *const dList, u32 start) { // Clear screen. - ee_printf("\x1b[2J"); + screenClean(); const u32 listLength = (dList->num - start > SCREEN_ROWS ? start + SCREEN_ROWS : dList->num); for(u32 i = start; i < listLength; i++) { - const char *const printStr = - (*dList->ptrs[i] == ENT_TYPE_FILE ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[36m %.51s"); + //const char *const printStr = + // (*dList->ptrs[i] == ENT_TYPE_FILE ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[36m %.51s"); - ee_printf(printStr, i - start, &dList->ptrs[i][1]); + //ee_printf(printStr, i - start, &dList->ptrs[i][1]); // TODO: use acf to display + ee_printf("\x1b[%dm", *dList -> ptrs[i] == ENT_TYPE_FILE ? 37 : 36); + acf_draw(20, LINENO_TO_Y(i-start), 320, 240, 292, &dList->ptrs[i][1] ); } } @@ -156,12 +163,21 @@ Result browseFiles(const char *const basePath, char selected[512]) s32 oldCursorPos = 0; while(1) { + // TODO: use acf to display if( dList->num > 0 ){ - ee_printf(*dList->ptrs[oldCursorPos] == ENT_TYPE_FILE ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[36m %.51s", oldCursorPos - windowPos, &dList->ptrs[oldCursorPos][1]); // Clear old cursor. - ee_printf("\x1b[%lu;H\x1b[33m>%.51s", cursorPos - windowPos, &dList->ptrs[cursorPos][1]); // Draw cursor. + //ee_printf(*dList->ptrs[oldCursorPos] == ENT_TYPE_FILE ? "\x1b[%lu;H\x1b[37m %.51s" : "\x1b[%lu;H\x1b[36m %.51s", oldCursorPos - windowPos, &dList->ptrs[oldCursorPos][1]); // Clear old cursor. + //ee_printf("\x1b[%lu;H\x1b[33m>%.51s", cursorPos - windowPos, &dList->ptrs[cursorPos][1]); // Draw cursor. + if( oldCursorPos != cursorPos ) + { + ee_printf("\x1b[%dm", *dList -> ptrs[oldCursorPos] == ENT_TYPE_FILE ? 37:36; + acf_draw( 20, LINENO_TO_Y(oldCursorPos-windowPos), 320, 240, 292, &dList->ptrs[oldCursorPos][1] ); + } + acf_draw( 20, LINENO_TO_Y(cursorPos-windowPos), 320, 240, 292, &dList->ptrs[cursorPos][1] ); } else { - ee_printf("\x1b[%lu;H\x1b[0m>%.51s", cursorPos - windowPos, ""); + //ee_printf("\x1b[%lu;H\x1b[0m>%.51s", cursorPos - windowPos, ""); + ee_printf("\x1b[0m"); + acf_draw( 20, LINENO_TO_Y(cursorPos-windowPos), 320, 240, 292, "" ); } u32 kDown; @@ -241,7 +257,7 @@ end: free(curDir); // Clear screen. - ee_printf("\x1b[2J\x1b[0m"); + screenClean(); return res; }