From 6625c886b53de413d1a9a332f65c1d7709f7712f Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Mon, 8 May 2023 08:14:02 -0700 Subject: [PATCH] tools: add ttf-convert This is a custom binary format for pre-rendering outline fonts as a collection of bitmaps. --- .gitignore | 1 + Makefile | 14 ++- tools/Makefile | 15 +++ tools/dejavusans.font | Bin 0 -> 37724 bytes tools/ttf-convert.cpp | 233 ++++++++++++++++++++++++++++++++++++++++++ vdp1/kana.cpp | 34 +++--- 6 files changed, 283 insertions(+), 14 deletions(-) create mode 100644 tools/Makefile create mode 100644 tools/dejavusans.font create mode 100644 tools/ttf-convert.cpp diff --git a/.gitignore b/.gitignore index df0be19..8530553 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ *.png *.out res/mai.data +tools/ttf-convert \ No newline at end of file diff --git a/Makefile b/Makefile index c67521b..33e4a38 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ vdp1/normal_sprite.elf: vdp1/normal_sprite.o res/mai00.data.o res/mai.data.pal.o vdp1/normal_sprite_color_bank.elf: vdp1/normal_sprite_color_bank.o res/mai00.data.o res/mai.data.pal.o -vdp1/kana.elf: vdp1/kana.o res/ipafont.bin.o sh/lib1funcs.o +vdp1/kana.elf: vdp1/kana.o res/ipapgothic.font.bin.o sh/lib1funcs.o res/mai.data: res/mai00.data res/mai01.data res/mai02.data res/mai03.data res/mai04.data res/mai05.data res/mai06.data res/mai07.data res/mai08.data res/mai09.data res/mai10.data res/mai11.data res/mai12.data res/mai13.data res/mai14.data res/mai15.data cat $(sort $^) > $@ @@ -44,11 +44,23 @@ vdp1/normal_sprite_animated.elf: vdp1/normal_sprite_animated.o res/mai.data.o re smpc/input_intback.elf: smpc/input_intback.o sh/lib1funcs.o +res/dejavusansmono.font.bin: tools/ttf-convert + ./tools/ttf-convert $(shell fc-match -f '%{file}' 'DejaVu Sans Mono') $@ + +res/ipapgothic.font.bin: tools/ttf-convert + ./tools/ttf-convert $(shell fc-match -f '%{file}' 'IPAPGothic') $@ + +smpc/input_keyboard.elf: smpc/input_keyboard.o sh/lib1funcs.o res/dejavusansmono.font.bin.o + +games/tetris.elf: games/tetris.o sh/lib1funcs.o + + # clean clean: clean-sh clean-sh: find -P \ -not -path './saturn/*' \ + -not -path './tools/*' \ -regextype posix-egrep \ -regex '.*\.(iso|o|bin|elf|cue)$$' \ -exec rm {} \; diff --git a/tools/Makefile b/tools/Makefile new file mode 100644 index 0000000..e654c0d --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,15 @@ +CFLAGS = -Og -Wall -Wextra -Werror -ggdb -Wno-error=unused-parameter -Wno-error=unused-variable +CFLAGS += $(shell pkg-config --cflags freetype2) +LDFLAGS = $(shell pkg-config --libs freetype2) + +all: ttf-convert + +%.o: %.cpp + $(CXX) $(CFLAGS) -c $< -o $@ + +%: %.o + $(CXX) $(LDFLAGS) $< -o $@ + +.SUFFIXES: + +# /usr/share/fonts/OTF/ipagp.ttf diff --git a/tools/dejavusans.font b/tools/dejavusans.font new file mode 100644 index 0000000000000000000000000000000000000000..0913295eba0f2f5f53e34471fabe39b4306f4f06 GIT binary patch literal 37724 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 Ud)mLM7ao2Peq`~c(-!~#KfC>Kg#Z8m literal 0 HcmV?d00001 diff --git a/tools/ttf-convert.cpp b/tools/ttf-convert.cpp new file mode 100644 index 0000000..926d0a9 --- /dev/null +++ b/tools/ttf-convert.cpp @@ -0,0 +1,233 @@ +#include +#include +#include +#include +#include + +#include +#include FT_FREETYPE_H + +/* +int load_bitmap_char(FT_Face face, FT_ULong char_code, uint8_t * buf) +{ + FT_Error error; + FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) { + printf("FT_Load_Glyph %s\n", FT_Error_String(error)); + return 0; + } + + assert(face->glyph->format == FT_GLYPH_FORMAT_BITMAP); + printf("num_grays %d\n", face->glyph->bitmap.num_grays); + printf("pitch %d\n", face->glyph->bitmap.pitch); + printf("width %d\n", face->glyph->bitmap.width); + assert(face->glyph->bitmap.width == 16); + printf("char_code %lx rows %d\n", char_code, face->glyph->bitmap.rows); + assert(face->glyph->bitmap.rows == 8 || (face->glyph->bitmap.rows % 8) == 0); + + for (int y = 0; y < (int)face->glyph->bitmap.rows; y++) { + uint8_t * row = &face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch]; + uint8_t row_out = 0; + for (int x = 0; x < face->glyph->bitmap.width; x++) { + int bit; + if (x < (int)face->glyph->bitmap.width) { + bit = (row[x / 8] >> (7 - (x % 8))) & 1; + } else { + bit = 0; + } + fprintf(stderr, bit ? "█" : " "); + row_out |= (bit << x); + } + fprintf(stderr, "\n"); + //buf[y] = row_out; + } + + return face->glyph->bitmap.rows * face->glyph->bitmap.pitch; +} +*/ + +// metrics are 26.6 fixed point +struct glyph_metrics { + int32_t width; + int32_t height; + int32_t horiBearingX; + int32_t horiBearingY; + int32_t horiAdvance; +} __attribute__ ((packed)); + +static_assert((sizeof (glyph_metrics)) == ((sizeof (int32_t)) * 5)); + +struct glyph_bitmap { + uint32_t offset; + uint32_t rows; + uint32_t width; + uint32_t pitch; +} __attribute__ ((packed)); + +static_assert((sizeof (glyph_bitmap)) == ((sizeof (uint32_t)) * 4)); + +struct glyph { + glyph_bitmap bitmap; + glyph_metrics metrics; +} __attribute__ ((packed)); + +static_assert((sizeof (glyph)) == ((sizeof (glyph_bitmap)) + (sizeof (glyph_metrics)))); + +struct font { + uint32_t glyph_index; + uint32_t bitmap_offset; + int32_t height; + int32_t max_advance; +} __attribute__ ((packed)); + +static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 4)); + +int32_t +load_outline_char(const FT_Face face, + const FT_ULong char_code, + glyph * glyph, + uint8_t * glyph_bitmaps, + const uint32_t bitmap_offset) +{ + FT_Error error; + FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); + + error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + if (error) { + printf("FT_Load_Glyph %s\n", FT_Error_String(error)); + return -1; + } + + assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); + + error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + if (error) { + printf("FT_Render_Glyph %s\n", FT_Error_String(error)); + return -1; + } + + if (!(face->glyph->bitmap.pitch > 0)) { + printf("%lx : pitch == 0\n", char_code); + return 0; + } + + uint32_t pitch = (face->glyph->bitmap.pitch + 8 - 1) & -8; + uint32_t bitmap_size = face->glyph->bitmap.rows * pitch; + + for (uint32_t y = 0; y < face->glyph->bitmap.rows; y++) { + for (uint32_t x = 0; x < face->glyph->bitmap.width; x++) { + uint8_t i = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; + glyph_bitmaps[bitmap_offset + (y * pitch + x)] = i >> 3; + } + } + + //memcpy(&glyph_bitmaps[bitmap_offset], face->glyph->bitmap.buffer, bitmap_size); + + glyph_bitmap& bitmap = glyph->bitmap; + bitmap.offset = bswap_32(bitmap_offset); + bitmap.rows = bswap_32(face->glyph->bitmap.rows); + bitmap.width = bswap_32(face->glyph->bitmap.width); + bitmap.pitch = bswap_32(pitch); + //printf("%lx: %d %d\n", char_code, pitch, face->glyph->bitmap.width); + assert((pitch % 8) == 0); + + glyph_metrics& metrics = glyph->metrics; + metrics.width = bswap_32(face->glyph->metrics.width); + metrics.height = bswap_32(face->glyph->metrics.height); + metrics.horiBearingX = bswap_32(face->glyph->metrics.horiBearingX); + metrics.horiBearingY = bswap_32(face->glyph->metrics.horiBearingY); + metrics.horiAdvance = bswap_32(face->glyph->metrics.horiAdvance); + + return bitmap_size; +} + +struct range { + uint32_t start; + uint32_t end; +}; + +int main(int argc, char *argv[]) +{ + FT_Library library; + FT_Face face; + FT_Error error; + + if (argc < 3) { + fprintf(stderr, "usage: %s [font-file-path] [output-file-path]\n", argv[0]); + return -1; + } + + error = FT_Init_FreeType(&library); + if (error) { + fprintf(stderr, "FT_Init_FreeType\n"); + return -1; + } + + error = FT_New_Face(library, argv[1], 0, &face); + if (error) { + fprintf(stderr, "FT_New_Face\n"); + return -1; + } + + /* + error = FT_Select_Size(face, 0); + if (error) { + fprintf(stderr, "FT_Select_Size: %s %d\n", FT_Error_String(error), error); + return -1; + } + */ + + error = FT_Set_Pixel_Sizes (face, 0, 30); + if (error) { + fprintf(stderr, "FT_Set_Pixel_Sizes: %s %d\n", FT_Error_String(error), error); + return -1; + } + + uint32_t start = 0x3000; + uint32_t end = 0x30ff; + //uint32_t start = 0x20; + //uint32_t end = 0x7f; + + glyph glyphs[(end - start) + 1]; + uint8_t glyph_bitmaps[1024 * 1024]; + memset(glyph_bitmaps, 0x00, 1024 * 1024); + uint32_t bitmap_offset = 0, glyph_index = 0; + int32_t bitmap_size; + + for (uint32_t char_code = start; char_code <= end; char_code++) { + bitmap_size = load_outline_char(face, + char_code, + &glyphs[glyph_index], + &glyph_bitmaps[0], + bitmap_offset); + if (bitmap_size < 0) { + fprintf(stderr, "load_outline_char error\n"); + return -1; + } + + bitmap_offset += bitmap_size; + assert(bitmap_offset < (sizeof (glyph_bitmaps))); + glyph_index++; + } + + font font; + font.glyph_index = bswap_32(glyph_index); + font.bitmap_offset = bswap_32(bitmap_offset); + font.height = bswap_32(face->size->metrics.height); + font.max_advance = bswap_32(face->size->metrics.max_advance); + + FT_Done_FreeType(library); + + printf("bitmap_offset %u\n", bitmap_offset); + printf("glyph_index %u\n", glyph_index); + + FILE * out = fopen(argv[2], "w"); + + fwrite(reinterpret_cast(&font), (sizeof (font)), 1, out); + fwrite(reinterpret_cast(&glyphs[0]), (sizeof (glyph)), glyph_index, out); + fwrite(reinterpret_cast(&glyph_bitmaps[0]), (sizeof (uint8_t)), bitmap_offset, out); + + fclose(out); +} diff --git a/vdp1/kana.cpp b/vdp1/kana.cpp index 7c8c949..2ef8aab 100644 --- a/vdp1/kana.cpp +++ b/vdp1/kana.cpp @@ -2,7 +2,7 @@ #include "vdp2.h" #include "vdp1.h" -extern void * _ipafont_data_start __asm("_binary_res_ipafont_bin_start"); +extern void * _ipafont_data_start __asm("_binary_res_ipapgothic_font_bin_start"); constexpr inline uint16_t rgb15_gray(uint32_t intensity) { @@ -11,7 +11,7 @@ constexpr inline uint16_t rgb15_gray(uint32_t intensity) | ((intensity & 31) << 0 ); // red } -void color_palette(uint32_t colors, uint32_t color_bank) +void vdp2_color_palette(uint32_t colors, uint32_t color_bank) { /* generate a palette of 32 grays */ @@ -80,6 +80,17 @@ struct glyph { glyph_metrics metrics; } __attribute__ ((packed)); +static_assert((sizeof (glyph)) == ((sizeof (glyph_bitmap)) + (sizeof (glyph_metrics)))); + +struct font { + uint32_t glyph_index; + uint32_t bitmap_offset; + int32_t height; + int32_t max_advance; +} __attribute__ ((packed)); + +static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 4)); + template void copy(T * dst, const T * src, int32_t n) noexcept { @@ -146,19 +157,16 @@ void main() constexpr uint32_t colors = 256; constexpr uint32_t color_bank = 0; // completely random and arbitrary value - uint8_t * data8 = reinterpret_cast(&_ipafont_data_start); - uint32_t * data32 = reinterpret_cast(&_ipafont_data_start); + uint8_t * data = reinterpret_cast(&_ipafont_data_start); - const uint32_t glyph_index = data32[0]; - const int32_t bitmap_offset = data32[1]; - const int32_t face_height = data32[2]; - const glyph * glyphs = reinterpret_cast(&data32[3]); + const font * font = reinterpret_cast(&data[0]); + const glyph * glyphs = reinterpret_cast(&data[(sizeof (struct font))]); // there are three 32-bit fields before the start of `glyphs` - const uint8_t * glyph_bitmaps = reinterpret_cast(&data8[((sizeof (int32_t)) * 3) + glyph_index * (sizeof (glyph))]); + const uint8_t * glyph_bitmaps = &data[(sizeof (struct font)) + ((sizeof (struct glyph)) * font->glyph_index)]; - top = character_address = pixel_data(top, glyph_bitmaps, bitmap_offset); + top = character_address = pixel_data(top, glyph_bitmaps, font->bitmap_offset); - color_palette(colors, color_bank); + vdp2_color_palette(colors, color_bank); // For color bank color, COLR is concatenated bitwise with pixel data. See // Figure 6.17 in the VDP1 manual. color_address = color_bank << 8; @@ -220,10 +228,10 @@ void main() cmd_ix, _string, ((sizeof (_string)) / (sizeof (_string[0]))) - 1); - + vdp1.vram.cmd[cmd_ix].CTRL = CTRL__END; cmd_ix++; - + // start drawing (execute the command list) on every frame vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE; }