mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-06 22:04:10 +08:00
387 lines
11 KiB
C
387 lines
11 KiB
C
/*
|
||
* 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;
|
||
|
||
// 绘制
|
||
for (int i = 0; i < bbx[1]; ++i)
|
||
{
|
||
for (int j = 0; j < bbx[0]; ++j)
|
||
{
|
||
int tx = cx+j, ty = cy-i;
|
||
if( 0 <= ty && ty < (int)height && 0 <= tx && tx < (int)width )
|
||
{
|
||
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));
|
||
*canvas = width;
|
||
|
||
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;
|
||
|
||
fClose(font);
|
||
return canvas;
|
||
}
|