433 lines
13 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"
#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_canvas_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;
}