tools: add ttf-convert
This is a custom binary format for pre-rendering outline fonts as a collection of bitmaps.
This commit is contained in:
parent
b092850c07
commit
6625c886b5
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@
|
|||||||
*.png
|
*.png
|
||||||
*.out
|
*.out
|
||||||
res/mai.data
|
res/mai.data
|
||||||
|
tools/ttf-convert
|
14
Makefile
14
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/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
|
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 $^) > $@
|
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
|
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: clean-sh
|
clean: clean-sh
|
||||||
clean-sh:
|
clean-sh:
|
||||||
find -P \
|
find -P \
|
||||||
-not -path './saturn/*' \
|
-not -path './saturn/*' \
|
||||||
|
-not -path './tools/*' \
|
||||||
-regextype posix-egrep \
|
-regextype posix-egrep \
|
||||||
-regex '.*\.(iso|o|bin|elf|cue)$$' \
|
-regex '.*\.(iso|o|bin|elf|cue)$$' \
|
||||||
-exec rm {} \;
|
-exec rm {} \;
|
||||||
|
15
tools/Makefile
Normal file
15
tools/Makefile
Normal file
@ -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
|
BIN
tools/dejavusans.font
Normal file
BIN
tools/dejavusans.font
Normal file
Binary file not shown.
233
tools/ttf-convert.cpp
Normal file
233
tools/ttf-convert.cpp
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <byteswap.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#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<void*>(&font), (sizeof (font)), 1, out);
|
||||||
|
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), glyph_index, out);
|
||||||
|
fwrite(reinterpret_cast<void*>(&glyph_bitmaps[0]), (sizeof (uint8_t)), bitmap_offset, out);
|
||||||
|
|
||||||
|
fclose(out);
|
||||||
|
}
|
@ -2,7 +2,7 @@
|
|||||||
#include "vdp2.h"
|
#include "vdp2.h"
|
||||||
#include "vdp1.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)
|
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
|
| ((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 */
|
/* generate a palette of 32 grays */
|
||||||
|
|
||||||
@ -80,6 +80,17 @@ struct glyph {
|
|||||||
glyph_metrics metrics;
|
glyph_metrics metrics;
|
||||||
} __attribute__ ((packed));
|
} __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 <typename T>
|
template <typename T>
|
||||||
void copy(T * dst, const T * src, int32_t n) noexcept
|
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 colors = 256;
|
||||||
constexpr uint32_t color_bank = 0; // completely random and arbitrary value
|
constexpr uint32_t color_bank = 0; // completely random and arbitrary value
|
||||||
|
|
||||||
uint8_t * data8 = reinterpret_cast<uint8_t*>(&_ipafont_data_start);
|
uint8_t * data = reinterpret_cast<uint8_t*>(&_ipafont_data_start);
|
||||||
uint32_t * data32 = reinterpret_cast<uint32_t*>(&_ipafont_data_start);
|
|
||||||
|
|
||||||
const uint32_t glyph_index = data32[0];
|
const font * font = reinterpret_cast<struct font*>(&data[0]);
|
||||||
const int32_t bitmap_offset = data32[1];
|
const glyph * glyphs = reinterpret_cast<struct glyph*>(&data[(sizeof (struct font))]);
|
||||||
const int32_t face_height = data32[2];
|
|
||||||
const glyph * glyphs = reinterpret_cast<glyph*>(&data32[3]);
|
|
||||||
// there are three 32-bit fields before the start of `glyphs`
|
// there are three 32-bit fields before the start of `glyphs`
|
||||||
const uint8_t * glyph_bitmaps = reinterpret_cast<uint8_t*>(&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
|
// For color bank color, COLR is concatenated bitwise with pixel data. See
|
||||||
// Figure 6.17 in the VDP1 manual.
|
// Figure 6.17 in the VDP1 manual.
|
||||||
color_address = color_bank << 8;
|
color_address = color_bank << 8;
|
||||||
@ -220,10 +228,10 @@ void main()
|
|||||||
cmd_ix,
|
cmd_ix,
|
||||||
_string,
|
_string,
|
||||||
((sizeof (_string)) / (sizeof (_string[0]))) - 1);
|
((sizeof (_string)) / (sizeof (_string[0]))) - 1);
|
||||||
|
|
||||||
vdp1.vram.cmd[cmd_ix].CTRL = CTRL__END;
|
vdp1.vram.cmd[cmd_ix].CTRL = CTRL__END;
|
||||||
cmd_ix++;
|
cmd_ix++;
|
||||||
|
|
||||||
// start drawing (execute the command list) on every frame
|
// start drawing (execute the command list) on every frame
|
||||||
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
|
vdp1.reg.PTMR = PTMR__PTM__FRAME_CHANGE;
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user