371 lines
11 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* 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;
}