awtk/tools/font_gen/font_gen.c
2024-03-27 11:36:42 +08:00

209 lines
5.8 KiB
C

/**
* File: font_gen.c
* Author: AWTK Develop Team
* Brief: bitmap font generator
*
* Copyright (c) 2018 - 2024 Guangzhou ZHIYUAN Electronics Co.,Ltd.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* License file for more details.
*
*/
/**
* History:
* ================================================================
* 2018-01-21 Li XianJing <xianjimli@hotmail.com> created
*
*/
#include <wctype.h>
#include "tkc/mem.h"
#include "tkc/utf8.h"
#include "tkc/fs.h"
#include "tkc/path.h"
#include "base/bitmap.h"
#include "common/utils.h"
#include "font_gen/font_gen.h"
#include "font_gen/preprocess_text.inc"
#include "base/assets_manager.h"
#include "font_loader/font_loader_stb.h"
#include "font_loader/font_loader_bitmap.h"
#define MAX_CHARS 100 * 1024
#define MAX_BUFF_SIZE 1 * 1024 * 1024
static int char_cmp(const void* a, const void* b) {
wchar_t c1 = *(wchar_t*)a;
wchar_t c2 = *(wchar_t*)b;
return c1 - c2;
}
ret_t font_gen(font_t* font, uint16_t font_size, glyph_format_t format, const char* str,
const char* output_filename, const char* theme, const char* res_dir) {
str_t tstr;
str_t name;
wbuffer_t wbuffer;
uint32_t size = 0;
str_init(&name, 0);
str_init(&tstr, 100000);
wbuffer_init_extendable(&wbuffer);
str = font_gen_expand_text(str, &tstr);
size = font_gen_buff(font, font_size, format, str, &wbuffer);
if (strstr(output_filename, ".bin") != NULL) {
file_write(output_filename, wbuffer.data, size);
} else {
char path[MAX_PATH + 1] = {0};
if (TK_STR_IS_NOT_EMPTY(res_dir)) {
str_append(&name, res_dir);
str_append(&name, "/");
}
path_basename_ex(output_filename, TRUE, path, sizeof(path));
str_append(&name, path);
output_res_c_source_ex(output_filename, theme, ASSET_TYPE_FONT, ASSET_TYPE_FONT_BMP,
wbuffer.data, size, name.str);
}
str_reset(&tstr);
str_reset(&name);
wbuffer_deinit(&wbuffer);
return RET_OK;
}
static ret_t fong_convert_alpha8_to_alpha4(glyph_t* from, glyph_t* to) {
uint32_t x = 0;
uint32_t y = 0;
const uint8_t* pfrom = from->data;
uint8_t* p = (uint8_t*)(to->data);
for (y = 0; y < from->h; y++) {
for (x = 0; x < from->w; x++) {
uint32_t i = x / 2;
if ((x % 2) == 0) {
p[i] = (pfrom[x] >> 4) & 0x0f;
} else {
p[i] |= pfrom[x] & 0xf0;
}
}
p += to->pitch;
pfrom += from->pitch;
}
return RET_OK;
}
static ret_t font_gen_glyph(font_t* font, glyph_format_t format, wchar_t c, font_size_t font_size,
glyph_t* g) {
static glyph_t gg;
static uint8_t buff[200 * 200];
memset(&gg, 0x00, sizeof(gg));
memset(buff, 0x00, sizeof(buff));
if (font_get_glyph(font, c, font_size, &gg) == RET_OK) {
if (!gg.pitch) {
gg.pitch = gg.w;
}
*g = gg;
if (format != gg.format) {
g->data = buff;
g->format = format;
log_debug("convert %d => %d\n", (int)(gg.format), (int)(format));
if (format == GLYPH_FMT_ALPHA4 && gg.format == GLYPH_FMT_ALPHA) {
g->pitch = (gg.pitch + 1) / 2;
return fong_convert_alpha8_to_alpha4(&gg, g);
} else if (c != ' ') {
assert(!"not supported format yet");
return RET_FAIL;
}
}
return RET_OK;
} else {
return RET_FAIL;
}
}
uint32_t font_gen_buff(font_t* font, uint16_t font_size, glyph_format_t format, const char* str,
wbuffer_t* wbuffer) {
int i = 0;
glyph_t g;
int size = 0;
wchar_t wstr[MAX_CHARS];
font_vmetrics_t vmetrics = font_get_vmetrics(font, font_size);
tk_utf8_to_utf16(str, wstr, MAX_CHARS);
size = wcslen(wstr);
qsort(wstr, size, sizeof(wchar_t), char_cmp);
size = unique(wstr, size);
int32_t header_size = sizeof(font_bitmap_header_t) + (size - 1) * sizeof(font_bitmap_index_t);
font_bitmap_header_t* header = (font_bitmap_header_t*)TKMEM_ALLOC(header_size);
memset(header, 0, header_size);
wbuffer_write_binary(wbuffer, header, header_size);
header->format = format;
header->char_nr = size;
header->font_size = (uint8_t)font_size;
header->ascent = vmetrics.ascent;
header->descent = vmetrics.descent;
header->line_gap = vmetrics.line_gap;
memset(&g, 0x00, sizeof(g));
for (i = 0; i < size; i++) {
wchar_t c = wstr[i];
header->index[i].c = c;
header->index[i].size = 0;
header->index[i].offset = wbuffer->cursor;
printf("%d/%d: 0x%04x format=%d w=%d h=%d pitch=%d\n", i, size, c, (int)format, (int)(g.w),
(int)(g.h), (int)(g.pitch));
if (font_gen_glyph(font, format, c, font_size, &g) == RET_OK) {
uint32_t data_size = g.pitch * g.h;
wbuffer_write_uint16(wbuffer, g.x);
wbuffer_write_uint16(wbuffer, g.y);
wbuffer_write_uint16(wbuffer, g.w);
wbuffer_write_uint16(wbuffer, g.h);
wbuffer_write_uint16(wbuffer, g.advance);
wbuffer_write_uint8(wbuffer, g.format);
wbuffer_write_uint8(wbuffer, g.pitch);
if (g.data != NULL) {
header->index[i].size = data_size;
wbuffer_write_binary(wbuffer, g.data, data_size);
}
if (g.format == GLYPH_FMT_MONO) {
bitmap_mono_dump(g.data, g.w, g.h);
}
} else if (c > 32 && c != 0xfeff) {
wchar_t arr[] = {c};
char utf8_arr[32] = {0};
memset(utf8_arr, 0x00, sizeof(utf8_arr));
tk_utf8_from_utf16(arr, utf8_arr, sizeof(utf8_arr));
printf("Warnning: not find char 0x%04x('%s') in TTF\n", c, utf8_arr);
header->index[i].offset = 0;
} else {
header->index[i].offset = 0;
}
}
size = wbuffer->cursor;
wbuffer->cursor = 0;
wbuffer_write_binary(wbuffer, header, header_size);
wbuffer->cursor = size;
TKMEM_FREE(header);
return size;
}