mirror of
https://gitee.com/anod/open_agb_firm.git
synced 2025-05-05 21:34:11 +08:00
!1 把dev_cheat的部分改动(基本上是acf)合并回去
* 修改好bug,改过的acf接口 * Merge branch 'dev_cheat' of https://gitee.com/anod/open_agb_firm into dev_cheat * acf修改接口 * Merge branch 'dev_cheat' of https://gitee.com/anod/open_agb_firm into dev_cheat * 修正:上个版本会访问非法内存导致段错误 * 补档:字体文件生成工具
This commit is contained in:
parent
a82c282ea9
commit
e484eb6202
@ -172,3 +172,7 @@ You may use this under the terms of the GNU General Public License GPL v3 or the
|
||||
* ...everyone who contributed to **3dbrew.org**
|
||||
|
||||
Copyright (C) 2021 derrek, profi200, d0k3
|
||||
|
||||
|
||||
Known Issue:
|
||||
盗版卡存档有问题
|
||||
|
@ -6,24 +6,64 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define ACFONT_NOT_FOUND 1
|
||||
#define ACFONT_INVALID 2
|
||||
#define ACFONT_READ_EOF 3
|
||||
#define ACFONT_NOT_SUPPORT 4
|
||||
#define ACFONT_MEM_EMPTY 5
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern int acf_set_font(const char *);
|
||||
extern const char *acf_draw(int, int, unsigned, unsigned, unsigned, uint16_t, const char *);
|
||||
#define INPUT(type) type
|
||||
#define OUTPUT(type) type*
|
||||
|
||||
#define ACF_LIT_POINT(x, y, w, h, islit) \
|
||||
{ \
|
||||
if (0 <= (x) && (x) < (w) && 0 <= (y) && (y) < (h)) \
|
||||
{ \
|
||||
if ((islit)) \
|
||||
{ \
|
||||
frame[ (h)*(x) + (h-1-y) ] = fg; \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
// error type
|
||||
#define ACFONT_NOT_FOUND -1
|
||||
#define ACFONT_INVALID -2
|
||||
#define ACFONT_READ_EOF -3
|
||||
#define ACFONT_NOT_SUPPORT -4
|
||||
#define ACFONT_MEM_EMPTY -5
|
||||
|
||||
typedef int acf_error_t;
|
||||
typedef void * acf_canvas_t;
|
||||
typedef void * acf_callerdata_t;
|
||||
typedef uint16_t acf_position_t;
|
||||
typedef uint16_t acf_rectedge_t;
|
||||
typedef uint8_t acf_color_t;
|
||||
typedef const char * acf_text_t;
|
||||
typedef uint16_t acf_counter_t;
|
||||
|
||||
typedef void (*acf_visitor_t)(
|
||||
INPUT(acf_callerdata_t) some_data_provide_by_caller,
|
||||
INPUT(acf_position_t) position_x_of_canvas,
|
||||
INPUT(acf_position_t) position_y_of_canvas,
|
||||
INPUT(acf_color_t) color_in_position_xy_of_canvas
|
||||
);
|
||||
|
||||
extern acf_error_t acf_initialize(
|
||||
INPUT(acf_text_t) filename_of_acf_font
|
||||
);
|
||||
|
||||
extern acf_canvas_t acf_get_canvas(
|
||||
INPUT(acf_rectedge_t) width_in_pixel_of_canvas,
|
||||
INPUT(acf_text_t) text_painting_in_utf8,
|
||||
|
||||
OUTPUT(acf_rectedge_t) using_width_in_pixel_of_canvas,
|
||||
OUTPUT(acf_counter_t) rendered_char_counting,
|
||||
OUTPUT(acf_text_t) text_not_rendered_in_utf8
|
||||
);
|
||||
|
||||
extern void acf_recycle(
|
||||
INPUT(acf_canvas_t) canvas_should_free;
|
||||
);
|
||||
|
||||
extern acf_canvas_t acf_use_canvas(
|
||||
INPUT(acf_canvas_t) canvas_should_use,
|
||||
INPUT(acf_visitor_t) pixel_visitor_for_canvas,
|
||||
INPUT(acf_callerdata_t) some_data_from_caller
|
||||
);
|
||||
|
||||
#undef INPUT
|
||||
#undef OUTPUT
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif //_ANOD_COMPILED_FONT_H_
|
||||
|
@ -23,7 +23,7 @@
|
||||
|
||||
|
||||
|
||||
#define GX_REGS_BASE (IO_MEM_ARM11_ONLY + 0x200000)
|
||||
#define GX_REGS_BASE (IO_MEM_ARM11_ONLY + 0x200000) // 10400000H
|
||||
#define REG_GX_GPU_CLK *((vu32*)(GX_REGS_BASE + 0x0004)) // ?
|
||||
|
||||
// PSC (memory fill) regs.
|
||||
|
Binary file not shown.
@ -10,13 +10,6 @@
|
||||
#include "arm11/fmt.h"
|
||||
#include "fs.h"
|
||||
|
||||
// 画点函数
|
||||
#ifndef ACF_LIT_POINT
|
||||
#error "ACF_LIT_POINT SHOULD BE DECLARE BEFORE USING GB2312 FONT"
|
||||
#endif
|
||||
|
||||
extern uint8_t canvas[];
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint16_t start;
|
||||
@ -27,8 +20,8 @@ typedef struct
|
||||
|
||||
typedef struct
|
||||
{
|
||||
uint8_t width;
|
||||
uint8_t height;
|
||||
acf_rectedge_t width;
|
||||
acf_rectedge_t height;
|
||||
int8_t offsetx;
|
||||
int8_t offsety;
|
||||
char *filename;
|
||||
@ -59,7 +52,7 @@ static ACFont gblfont = {
|
||||
// 2 使用该字体在一个原点范围内渲染一行文字
|
||||
|
||||
// 设置字体名称
|
||||
int acf_set_font(const char *acfile)
|
||||
acf_error_t acf_initialize(acf_text_t acfile)
|
||||
{
|
||||
// open the font file
|
||||
FHandle font;
|
||||
@ -241,7 +234,7 @@ static inline int readS6(uint8_t *p, int index)
|
||||
}
|
||||
|
||||
#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, unsigned *width_max, uint8_t *ram)
|
||||
static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, uint32_t code, uint8_t *ram)
|
||||
{
|
||||
uint32_t readed;
|
||||
|
||||
@ -287,11 +280,11 @@ static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, u
|
||||
|
||||
// render
|
||||
// 从上往下(y从大到小,x从小到大)进行绘制
|
||||
int px = *x, py = 0-gblfont.offsety; // px/py不参与计算位置
|
||||
int px = *x, py = -1-gblfont.offsety; // 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)))
|
||||
if ( cx + bbx[0] >= (int)width )
|
||||
return 1;
|
||||
|
||||
// 绘制
|
||||
@ -299,11 +292,12 @@ static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, u
|
||||
{
|
||||
for (int j = 0; j < bbx[0]; ++j)
|
||||
{
|
||||
if( 0 <= i && i < (int)height && 0 <= j && j < (int)width )
|
||||
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(cx+j, cy-i, width) );
|
||||
SET_AT_POS( ram, PIX_IN_LINE(tx, ty, width) );
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -314,62 +308,78 @@ static int render_unicode(FHandle fd, int *x, unsigned width, unsigned height, u
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define ACFONT_CANVAS_MEMSIZE( w, h ) ( ( (w)*(h) + BIT_PER_BYTE-1 )/BIT_PER_BYTE+sizeof(acf_rectedge_t) )
|
||||
#define ACFONT_CANVAS_WIDTH( p ) (*(acf_rectedge_t*)(p))
|
||||
#define ACFONT_CANVAS_PIXEL( p ) ((uint8_t*)(p)+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_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;
|
||||
for( acf_position_t i=0; i < height; i++ )
|
||||
{
|
||||
acf_position_t dy = height - 1 - i;
|
||||
for( register acf_position_t j=0; j < 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
|
||||
const char *acf_draw(int x, int y, unsigned width, unsigned height, unsigned maxwidth, uint16_t color, const char *utf8_line)
|
||||
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;
|
||||
int linex = 0;
|
||||
if( gblfont.filename == NULL || RES_OK != fOpen(&font, gblfont.filename, FA_OPEN_EXISTING | FA_READ) )
|
||||
{
|
||||
// log
|
||||
return utf8_line;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint32_t unicode;
|
||||
if( maxwidth == 0 ) maxwidth = width;
|
||||
unsigned *option = width == 0 ? NULL : &maxwidth;
|
||||
int linex = 0;
|
||||
int rendered_count = 0;
|
||||
const char *utf8_line = text;
|
||||
|
||||
const int ramsize = gblfont.height * maxwidth + BIT_PER_BYTE - 1 / BIT_PER_BYTE;
|
||||
uint8_t *localram = malloc( ramsize );
|
||||
memset( localram, 0, ramsize );
|
||||
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, maxwidth, height, unicode, option, localram);
|
||||
if (error > 0 ) break;
|
||||
else if(error < 0 ) error = render_unicode(font, &linex, maxwidth, height, '?', option, localram);
|
||||
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( localram );
|
||||
free( canvas );
|
||||
fClose(font);
|
||||
return utf8_line;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
utf8_line = next;
|
||||
++rendered_count;
|
||||
}
|
||||
|
||||
// copy back to canvas
|
||||
uint16_t *frame = consoleGet()->frameBuffer;
|
||||
uint16_t fg = color;
|
||||
for( int i=0; i < gblfont.height; ++i )
|
||||
{
|
||||
for( int j=0; j < (int)maxwidth; ++j )
|
||||
{
|
||||
if( BIT_AT_POS(localram, PIX_IN_LINE(j, i, maxwidth)) )
|
||||
{
|
||||
ACF_LIT_POINT(x+j, y-(gblfont.height-1-i), (int)width, (int)height, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free( localram );
|
||||
if( realwidth != NULL ) *realwidth = linex;
|
||||
if( renderedcnt != NULL ) *renderedcnt = rendered_count;
|
||||
if( rest != NULL ) *rest = utf8_line;
|
||||
|
||||
fClose(font);
|
||||
return NULL;
|
||||
return canvas;
|
||||
}
|
||||
|
@ -58,6 +58,38 @@ typedef struct
|
||||
char *ptrs[MAX_DIR_ENTRIES]; // For fast sorting.
|
||||
} DirList;
|
||||
|
||||
void set_screen_color( acf_callerdata_t data, acf_position_t tx, acf_position_t ty, acf_color_t b )
|
||||
{
|
||||
if( b == 0 ) return;
|
||||
|
||||
int *draw_data = data;
|
||||
u16 *frame = consoleGet()->frameBuffer;
|
||||
|
||||
int x = tx + draw_data[0];
|
||||
int y = ty + draw_data[1];
|
||||
if( 0 <= x && x < draw_data[2] && 0 <= y && y < draw_data[3] ){
|
||||
frame[ x*draw_data[3] + (draw_data[3]-1-y) ] = (u16)draw_data[4];
|
||||
}
|
||||
}
|
||||
|
||||
const char *acf_draw(int x, int y, int width, int height, int maxwidth, u16 color, const char* text)
|
||||
{
|
||||
uint8_t draw_data[sizeof(int)*5];
|
||||
|
||||
int *draw_data_int = (int*)&draw_data;
|
||||
draw_data_int[0] = x;
|
||||
draw_data_int[1] = y;
|
||||
draw_data_int[2] = width;
|
||||
draw_data_int[3] = height;
|
||||
draw_data_int[4] = color;
|
||||
|
||||
const char *retval = text;
|
||||
acf_canvas_t canvas = acf_get_canvas(maxwidth, text, NULL, NULL, &retval);
|
||||
if( !canvas ) return retval;
|
||||
acf_recycle( acf_use_canvas(canvas, set_screen_color, draw_data) );
|
||||
return retval;
|
||||
}
|
||||
|
||||
int dlistCompare(const void *a, const void *b)
|
||||
{
|
||||
const char *entA = *(char**)a;
|
||||
@ -171,7 +203,7 @@ Result browseFiles(const char *const basePath, char selected[512])
|
||||
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.
|
||||
if( oldCursorPos != cursorPos && windowPos <= oldCursorPos && oldCursorPos < windowPos + SCREEN_ROWS )
|
||||
if( oldCursorPos != cursorPos && windowPos <= (u32)oldCursorPos && (u32)oldCursorPos < windowPos + SCREEN_ROWS )
|
||||
{
|
||||
const uint8_t fg = *dList -> ptrs[oldCursorPos] == ENT_TYPE_FILE ? 7:6;
|
||||
acf_draw( CLEFTMARGIN, LINENO_TO_Y(oldCursorPos-windowPos), CWIDTH, CHEIGHT, CLINELIMIT, consoleGetRGB565Color(fg), &dList->ptrs[oldCursorPos][1] );
|
||||
|
@ -526,6 +526,8 @@ static void gbaGfxHandler(void *args)
|
||||
}
|
||||
GX_processCommandList(listSize, list);
|
||||
GFX_waitForP3D();
|
||||
// 地址0x18180000保存的是360x240大小的贴图数据
|
||||
// 地址0x18200000保存的是512x512大小的GBA的240x160贴图数据
|
||||
GX_displayTransfer((u32*)(0x18180000 + (16 * 240 * 3)), 368u<<16 | 240u,
|
||||
GFX_getFramebuffer(SCREEN_TOP) + (16 * 240 * 3), 368u<<16 | 240u, 1u<<12 | 1u<<8);
|
||||
GFX_waitForPPF();
|
||||
@ -684,7 +686,7 @@ Result oafParseConfigEarly(void)
|
||||
// Create the saves folder.
|
||||
if((res = fMkdir(OAF_SAVE_DIR)) != RES_OK && res != RES_FR_EXIST) break;
|
||||
|
||||
if((res = acf_set_font("wqy11.fnt")) != RES_OK ) break;
|
||||
if((res = acf_initialize("wqy11.fnt")) != RES_OK ) break;
|
||||
|
||||
// Parse the config.
|
||||
res = parseOafConfig("config.ini", true);
|
||||
@ -743,7 +745,7 @@ Result oafInitAndRun(void)
|
||||
|
||||
// Initialize the legacy frame buffer and frame handler.
|
||||
const KHandle frameReadyEvent = createEvent(false);
|
||||
LGYFB_init(frameReadyEvent, g_oafConfig.scaler); // Setup Legacy Framebuffer.
|
||||
LGYFB_init(frameReadyEvent, g_oafConfig.scaler); // 这里把GBA的输出转换成0x18200000处512x512大小的纹理
|
||||
patchGbaGpuCmdList(g_oafConfig.scaler);
|
||||
createTask(0x800, 3, gbaGfxHandler, (void*)frameReadyEvent);
|
||||
g_frameReadyEvent = frameReadyEvent;
|
||||
|
310
tools/acf-builder/buildacf.js
Normal file
310
tools/acf-builder/buildacf.js
Normal file
@ -0,0 +1,310 @@
|
||||
const {open} = require("fs/promises");
|
||||
const {argv} = require("process");
|
||||
|
||||
// ACF文件格式:
|
||||
// 0-3:ACFv ACF为固定字符串,v表示版本号,从1开始
|
||||
// 4-7:font bounding 来自bdf文件
|
||||
// 8-9: fragment items size 字体数据开始的位置
|
||||
// 10-:fragment items 分段描述数据,一个分段的unicode是连续的
|
||||
//
|
||||
// 分段描述数据内容
|
||||
// 0-1:start unicode 本分段第一个unicode
|
||||
// 2-3:end unicode 本分段最后一个unicode
|
||||
// 5: padding size for unicode 本分段每个unicode的字体数据占用的字节长度
|
||||
// 6: bbxd count in this fragment 本分段总共用到的bbxd种类
|
||||
// other: 4*(bbxd count) 本分段用到的所有bbxd的原始数据
|
||||
//
|
||||
// bbxd数据格式:
|
||||
// 4个6字节的整数(-32 ~ 31),表示BBX对应的内容,加上一个6字节的整数表示dwidth的第一个数据
|
||||
//
|
||||
// 字体数据
|
||||
// 同一个fragment里面的所有unicode长度都是padding size,如果实际数据少的会补齐0
|
||||
// 如果fragment的bbxd count为1,则字体数据就是直接boundingbox的所有点阵数据
|
||||
// 如果fragment的bbxd count大于1,则字体数据是1字节的bbxd索引,后接boundingbox的所有点阵数据
|
||||
|
||||
const helper = () => {
|
||||
const setBitAt = (bitarr, pos) => {
|
||||
let byteIndex = Math.floor( pos / 8 );
|
||||
let bitOffset = pos % 8;
|
||||
bitarr[ byteIndex ] |= 1 << bitOffset;
|
||||
}
|
||||
const testBit = (byte, pos) => byte & ( 1 << pos);
|
||||
const putS6 = (arr, val, pos) => {
|
||||
// sign bit
|
||||
if( val < 0 ) {
|
||||
setBitAt( arr, pos );
|
||||
val = -val;
|
||||
}
|
||||
++pos;
|
||||
|
||||
// digits
|
||||
for( let i=1<<4; i > 0; i>>=1, pos++ ){
|
||||
if( val & i ) {
|
||||
setBitAt(arr, pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
return {setBitAt, testBit, putS6}
|
||||
}
|
||||
|
||||
const createDescript = () => {
|
||||
let numChars, boundingbox;
|
||||
let glyphs = []
|
||||
let charGlyph = {}
|
||||
let fragments = []
|
||||
|
||||
const apply = async ( cmd, params ) => {
|
||||
if( cmd == "chars" ) {
|
||||
[numChars] = params.map( n => Number(n) );
|
||||
}
|
||||
else if( cmd == "fontboundingbox" ) {
|
||||
boundingbox = params.map( n => Number(n) );
|
||||
}
|
||||
else if( cmd == "startchar" ){
|
||||
charGlyph = {};
|
||||
|
||||
if( fragments.length == 0 ){
|
||||
fragments.push({ padding: 0, bbxd: new Map() });
|
||||
}
|
||||
}
|
||||
else if( cmd == "encoding" ){
|
||||
charGlyph.code = Number(params[0]);
|
||||
}
|
||||
else if( cmd == "dwidth" ){
|
||||
charGlyph.dwidth = params.map( n => Number(n) );
|
||||
}
|
||||
else if( cmd == "bbx" ){
|
||||
charGlyph.bbx = params.map( n => Number(n) );
|
||||
}
|
||||
else if( cmd == "bitmap" ){
|
||||
charGlyph.recoding = true;
|
||||
charGlyph.data = [];
|
||||
}
|
||||
else if( cmd == "endchar" ){
|
||||
const {recoding, code, ...value} = charGlyph;
|
||||
const [w, h] = charGlyph.bbx;
|
||||
|
||||
let fragment = fragments.at(-1);
|
||||
if( !("start" in fragment) ){
|
||||
fragment.start = code;
|
||||
}
|
||||
else if( glyphs.at(-1).code + 1 != code ){// break
|
||||
fragment.end = glyphs.at(-1).code;
|
||||
|
||||
// new fragment
|
||||
fragment = { start: code, padding: 0, bbxd: new Map() };
|
||||
fragments.push( fragment );
|
||||
}
|
||||
|
||||
if( w*h > fragment.padding ){
|
||||
fragment.padding = w*h;
|
||||
}
|
||||
|
||||
const key = JSON.stringify( [...value.bbx, value.dwidth[0]] )
|
||||
if( !fragment.bbxd.has(key) ){
|
||||
fragment.bbxd.set( key, fragment.bbxd.size );
|
||||
}
|
||||
|
||||
charGlyph = {};
|
||||
glyphs.push( {...value, code, fragment} );
|
||||
}
|
||||
else if( cmd == "endfont" ){
|
||||
const fragment = fragments.at(-1);
|
||||
fragment.end = glyphs.at(-1).code;
|
||||
}
|
||||
else if( charGlyph.recoding ){
|
||||
charGlyph.data.push( parseInt(cmd, 16) );
|
||||
}
|
||||
}
|
||||
const save = async file => {
|
||||
const { setBitAt, testBit, putS6 } = helper();
|
||||
const packHead = () => {
|
||||
const buffer = new ArrayBuffer(6);
|
||||
const view = new DataView(buffer);
|
||||
view.setUint16(0, numChars, true);
|
||||
boundingbox.forEach( (c, i) => view.setInt8(2+i, c) )
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
const packHeadV2 = fragSize => {
|
||||
const buffer = new ArrayBuffer(10);
|
||||
const view = new DataView(buffer);
|
||||
view.setUint8(0, "A".charCodeAt())
|
||||
view.setUint8(1, "C".charCodeAt())
|
||||
view.setUint8(2, "F".charCodeAt())
|
||||
view.setUint8(3, 2)//v2
|
||||
boundingbox.forEach( (c, i) => view.setInt8(4+i, c) )
|
||||
view.setUint16(8, fragSize, true)
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
const packGlyphs = () => {
|
||||
return glyphs.map( glyph => [packChar(glyph), packPixel(glyph), glyph] )
|
||||
}
|
||||
const packChar = glyph => {
|
||||
const buffer = new Uint8Array(4);
|
||||
const bitwid = 6;
|
||||
glyph.bbx.forEach( (c,i) => putS6(buffer, c, bitwid*i) );
|
||||
putS6( buffer, glyph.dwidth[0], bitwid*4 );
|
||||
return buffer;
|
||||
}
|
||||
const packPixel = glyph => {
|
||||
const [w, h] = glyph.bbx;
|
||||
const {padding} = glyph.fragment;
|
||||
const nbyte = Math.ceil( padding / 8 );
|
||||
const buffer = new Uint8Array( nbyte );
|
||||
const base = 8 * ( (7+w) >> 3 ) - 1;
|
||||
for( let j = 0; j < h; ++j ){
|
||||
let linepixel = glyph.data[j];
|
||||
for( let i = 0; i < w; ++i ){
|
||||
if( testBit(linepixel, base-i) ){
|
||||
setBitAt( buffer, j*w+i )
|
||||
}
|
||||
}
|
||||
}
|
||||
return buffer;
|
||||
}
|
||||
const packIndex = fonts => {
|
||||
const buffer = new ArrayBuffer( numChars * 5 );
|
||||
const view = new DataView( buffer );
|
||||
let offset = 0;
|
||||
glyphs.forEach( (c, i) => {
|
||||
view.setUint16( i*5, c.code, true );
|
||||
view.setUint8( i*5 + 2, offset >> 16 );
|
||||
view.setUint8( i*5 + 3, offset & 0xFF );
|
||||
view.setUint8( i*5 + 4, (offset & 0xFFFF) >> 8 );
|
||||
offset += 4 + fonts[i][1].length;
|
||||
})
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
const packFragments = () => {
|
||||
const bitwid = 6;
|
||||
const serials = fragments.map( frag => {
|
||||
const buffer = new ArrayBuffer( 6+4*frag.bbxd.size );
|
||||
const view = new DataView( buffer );
|
||||
const nbyte = Math.ceil( frag.padding / 8 );
|
||||
view.setUint16( 0, frag.start, true );
|
||||
view.setUint16( 2, frag.end, true );
|
||||
view.setUint8( 4, frag.bbxd.size > 1 ? 1+nbyte : nbyte );
|
||||
view.setUint8( 5, frag.bbxd.size );
|
||||
|
||||
frag.bbxd.forEach( (idx, key) => {
|
||||
const arr = new Uint8Array(buffer, 6+idx*4, 4);
|
||||
const embeded = JSON.parse( key );
|
||||
embeded.forEach( (c,i)=>putS6(arr, c, bitwid*i) )
|
||||
} )
|
||||
return new Uint8Array(buffer);
|
||||
})
|
||||
return serials;
|
||||
}
|
||||
|
||||
/*
|
||||
* old code: pack for acfv1
|
||||
const head = packHead();
|
||||
const glyph = packGlyphs();
|
||||
const index = packIndex( glyph );
|
||||
|
||||
let out = await open(file, "w");
|
||||
await out.write(head);
|
||||
await out.write(index);
|
||||
let jobs = Promise.resolve();
|
||||
glyph.forEach( ([char, pixel]) => {
|
||||
const combine = Uint8Array.from([...char, ...pixel])
|
||||
jobs = jobs.then( () => out.write(combine) )
|
||||
} )
|
||||
await jobs;
|
||||
await out.close();
|
||||
*/
|
||||
|
||||
const index = packFragments();
|
||||
const head = packHeadV2( index.reduce( (r,b)=>r+b.length, 0 ) );
|
||||
const glyph = packGlyphs();
|
||||
|
||||
let out = await open(file, "w");
|
||||
await out.write( head );
|
||||
let jobs = Promise.resolve();
|
||||
index.forEach( buffer => {
|
||||
jobs = jobs.then( () => out.write(buffer) )
|
||||
} );
|
||||
await jobs;
|
||||
|
||||
jobs = Promise.resolve();
|
||||
glyph.forEach( ([char, pixel, glyph]) => {
|
||||
const {fragment} = glyph;
|
||||
if( fragment.bbxd.size == 1 ) jobs = jobs.then( () => out.write(pixel) )
|
||||
else {
|
||||
const key = JSON.stringify( [...glyph.bbx, glyph.dwidth[0]] )
|
||||
const combine = Uint8Array.from( [fragment.bbxd.get(key), ...pixel] )
|
||||
jobs = jobs.then( () => out.write(combine) )
|
||||
}
|
||||
} )
|
||||
await jobs;
|
||||
await out.close();
|
||||
}
|
||||
return { apply, save }
|
||||
}
|
||||
|
||||
const createReader = file => {
|
||||
const newline = "\n".charCodeAt();
|
||||
|
||||
let buffer = new Uint8Array(400);
|
||||
let cursor = buffer.length;
|
||||
let line = ""
|
||||
const readline = async () => {
|
||||
if( cursor == buffer.length ){
|
||||
cursor = 0;
|
||||
await file.read( buffer, cursor, buffer.length )
|
||||
}
|
||||
|
||||
let chars = []
|
||||
let fullline = null;
|
||||
while( cursor < buffer.length ){
|
||||
const char = buffer[cursor++];
|
||||
if( char == newline ) {
|
||||
fullline = `${line}${String.fromCharCode(...chars)}`;
|
||||
break;
|
||||
}
|
||||
|
||||
chars.push( char )
|
||||
}
|
||||
|
||||
if( fullline ){
|
||||
line = ""; // reset line
|
||||
return fullline.length > 0 ? fullline.split(" ").map( (word,i) => i==0 ? word.toLowerCase():word ) : [null];
|
||||
}
|
||||
else {
|
||||
line = `${line}${String.fromCharCode(...chars)}`;
|
||||
return await readline()
|
||||
}
|
||||
}
|
||||
return readline;
|
||||
}
|
||||
|
||||
const convert = async fname => {
|
||||
let bdf = await open( fname, "r" );
|
||||
|
||||
const readline = createReader( bdf );
|
||||
const bdfdesc = createDescript();
|
||||
|
||||
while( true ){
|
||||
let [cmd, ...params] = await readline();
|
||||
|
||||
await bdfdesc.apply( cmd, params )
|
||||
|
||||
if( cmd == "endfont" ) break;
|
||||
}
|
||||
|
||||
await bdf.close();
|
||||
await bdfdesc.save( fname.replace(".bdf", ".acf") );
|
||||
}
|
||||
|
||||
const start = async params => {
|
||||
if( params.length < 1 ){
|
||||
console.log("Usage: buildact file [file2] [...]")
|
||||
return;
|
||||
}
|
||||
|
||||
const formatter = new Intl.DateTimeFormat("zh-CN", {dateStyle: "short", timeStyle: "medium", timeZone: "Asia/Shanghai"})
|
||||
globalThis.debug = log => console.log( `[DEBUG][${formatter.format(new Date())}]${log}` )
|
||||
await Promise.all( params.map( file => convert(file) ) )
|
||||
}
|
||||
|
||||
start( argv.slice(2) );
|
Loading…
x
Reference in New Issue
Block a user