From b07f6575ba41ec0bd533346bf17cea06abc60a2d Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Sat, 30 Sep 2023 09:11:21 +1000 Subject: [PATCH] WIP on very basic platform independent system font backend --- misc/bitmap_font_gen.cs | 41 +++++++ misc/dreamcast/Makefile | 9 +- src/SystemFonts.c | 219 ++++++++++++++++++++++++++++++++++++++ third_party/gldc/Makefile | 10 +- 4 files changed, 271 insertions(+), 8 deletions(-) create mode 100644 misc/bitmap_font_gen.cs diff --git a/misc/bitmap_font_gen.cs b/misc/bitmap_font_gen.cs new file mode 100644 index 000000000..153f87ecb --- /dev/null +++ b/misc/bitmap_font_gen.cs @@ -0,0 +1,41 @@ +void Main() +{ + Console.WriteLine("static cc_uint8 font_bitmap[][8] = {"); + using (Bitmap bmp = new Bitmap(@"C:\classicube-dev\default.png")) + { + for (int CY = 0; CY < 16; CY++) + for (int CX = 0; CX < 16; CX++) + DecodeTile(bmp, CX, CY); + } + Console.WriteLine("}"); +} + +static void DecodeTile(Bitmap bmp, int cx, int cy) { + int c = (cy << 4) | cx; + if (c <= 32 || c >= 127) return; + + int X = cx * 8, Y = cy * 8; + + Console.Write("\t{ "); + + for (int y = Y; y < Y + 8; y++) { + uint mask = 0; + int shift = 0; + + for (int x = X; x < X + 8; x++, shift++) { + Color P = bmp.GetPixel(x, y); + + if (P.A == 0) { + mask |= 0u << shift; + } else if (P.R == 255 && P.G == 255 && P.B == 255 && P.A == 255) { + mask |= 1u << shift; + } else { + throw new InvalidOperationException("unsupported colour" + P); + } + } + + string suffix = y == Y + 7 ? " " : ","; + Console.Write("0x" + mask.ToString("X2") + suffix); + } + Console.WriteLine("}, /* " + (char)c + " */"); +} diff --git a/misc/dreamcast/Makefile b/misc/dreamcast/Makefile index 17b27ac69..ab37c445c 100644 --- a/misc/dreamcast/Makefile +++ b/misc/dreamcast/Makefile @@ -4,8 +4,10 @@ SOURCE_DIRS := src third_party/bearssl/src C_FILES := $(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.c)) OBJS := $(addprefix $(BUILD_DIR)/, $(notdir $(C_FILES:%.c=%.o))) CFLAGS :=-g -O1 -pipe -fno-math-errno -Ithird_party/bearssl/inc + +GLDC_LIB=third_party/gldc/libGLdc.a LDFLAGS=-g -LIBS=-lm +LIBS=-lm $(GLDC_LIB) TARGET := ClassiCube-dc @@ -13,10 +15,13 @@ ifeq ($(strip $(KOS_BASE)),) $(error "Please set KOS variables in your environment.") endif -default: $(BUILD_DIR) $(TARGET).cdi +default: $(GLDC_LIB) $(BUILD_DIR) $(TARGET).cdi $(BUILD_DIR): mkdir -p $(BUILD_DIR) + +$(GLDC_LIB): + $(MAKE) -C third_party/gldc $(BUILD_DIR)/%.o: src/%.c kos-cc $(CFLAGS) -c $< -o $@ diff --git a/src/SystemFonts.c b/src/SystemFonts.c index 506dc7846..b806bbb81 100644 --- a/src/SystemFonts.c +++ b/src/SystemFonts.c @@ -1419,4 +1419,223 @@ void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int } } } +#else + +#define SysFont_ValidChar(c) ((c) > 32 && (c) < 127) +#define SysFont_ToIndex(c) ((c) - 33) /* First valid char is ! */ +#define SPACE_WIDTH 2 +#define CELL_SIZE 8 + +#define SysFont_GetRows(c) (SysFont_ValidChar(c) ? font_bitmap[SysFont_ToIndex(c)] : missing_cell) + +static cc_uint8 missing_cell[CELL_SIZE] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF +}; + +/* 8x8 font bitmap, represented with 1 bit for each pixel */ +/* Source: Goodly's texture pack for ClassiCube */ +static cc_uint8 font_bitmap[][CELL_SIZE] = { + { 0x01,0x01,0x01,0x01,0x01,0x00,0x01,0x00 }, /* ! */ + { 0x05,0x05,0x05,0x00,0x00,0x00,0x00,0x00 }, /* " */ + { 0x0A,0x0A,0x1F,0x0A,0x1F,0x0A,0x0A,0x00 }, /* # */ + { 0x04,0x1F,0x01,0x1F,0x10,0x1F,0x04,0x00 }, /* $ */ + { 0x00,0x21,0x11,0x08,0x04,0x22,0x21,0x00 }, /* % */ + { 0x0C,0x12,0x0C,0x2E,0x19,0x11,0x2E,0x00 }, /* & */ + { 0x01,0x01,0x01,0x00,0x00,0x00,0x00,0x00 }, /* ' */ + { 0x04,0x02,0x01,0x01,0x01,0x02,0x04,0x00 }, /* ( */ + { 0x01,0x02,0x04,0x04,0x04,0x02,0x01,0x00 }, /* ) */ + { 0x00,0x02,0x07,0x02,0x05,0x00,0x00,0x00 }, /* * */ + { 0x00,0x04,0x04,0x1F,0x04,0x04,0x00,0x00 }, /* + */ + { 0x00,0x00,0x00,0x00,0x00,0x02,0x02,0x01 }, /* , */ + { 0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00 }, /* - */ + { 0x00,0x00,0x00,0x00,0x00,0x01,0x01,0x00 }, /* . */ + { 0x08,0x08,0x04,0x04,0x02,0x02,0x01,0x00 }, /* / */ + { 0x06,0x09,0x0D,0x0B,0x09,0x09,0x06,0x00 }, /* 0 */ + { 0x02,0x03,0x02,0x02,0x02,0x02,0x07,0x00 }, /* 1 */ + { 0x06,0x09,0x08,0x04,0x02,0x09,0x0F,0x00 }, /* 2 */ + { 0x06,0x09,0x08,0x06,0x08,0x09,0x06,0x00 }, /* 3 */ + { 0x05,0x05,0x05,0x0F,0x04,0x04,0x04,0x00 }, /* 4 */ + { 0x0F,0x01,0x07,0x08,0x08,0x09,0x06,0x00 }, /* 5 */ + { 0x06,0x09,0x01,0x07,0x09,0x09,0x06,0x00 }, /* 6 */ + { 0x0F,0x08,0x08,0x04,0x04,0x02,0x02,0x00 }, /* 7 */ + { 0x06,0x09,0x09,0x06,0x09,0x09,0x06,0x00 }, /* 8 */ + { 0x06,0x09,0x09,0x0E,0x08,0x09,0x06,0x00 }, /* 9 */ + { 0x00,0x01,0x01,0x00,0x00,0x01,0x01,0x00 }, /* : */ + { 0x00,0x02,0x02,0x00,0x00,0x02,0x02,0x01 }, /* ; */ + { 0x00,0x04,0x02,0x01,0x02,0x04,0x00,0x00 }, /* < */ + { 0x00,0x00,0x1F,0x00,0x00,0x1F,0x00,0x00 }, /* = */ + { 0x00,0x01,0x02,0x04,0x02,0x01,0x00,0x00 }, /* > */ + { 0x07,0x09,0x08,0x04,0x02,0x00,0x02,0x00 }, /* ? */ + { 0x0E,0x11,0x1D,0x1D,0x1D,0x01,0x0E,0x00 }, /* @ */ + { 0x06,0x09,0x09,0x0F,0x09,0x09,0x09,0x00 }, /* A */ + { 0x07,0x09,0x09,0x07,0x09,0x09,0x07,0x00 }, /* B */ + { 0x06,0x09,0x01,0x01,0x01,0x09,0x06,0x00 }, /* C */ + { 0x07,0x09,0x09,0x09,0x09,0x09,0x07,0x00 }, /* D */ + { 0x0F,0x01,0x01,0x07,0x01,0x01,0x0F,0x00 }, /* E */ + { 0x0F,0x01,0x01,0x07,0x01,0x01,0x01,0x00 }, /* F */ + { 0x06,0x09,0x01,0x0D,0x09,0x09,0x06,0x00 }, /* G */ + { 0x09,0x09,0x09,0x0F,0x09,0x09,0x09,0x00 }, /* H */ + { 0x07,0x02,0x02,0x02,0x02,0x02,0x07,0x00 }, /* I */ + { 0x08,0x08,0x08,0x08,0x08,0x09,0x07,0x00 }, /* J */ + { 0x09,0x09,0x05,0x03,0x05,0x09,0x09,0x00 }, /* K */ + { 0x01,0x01,0x01,0x01,0x01,0x01,0x0F,0x00 }, /* L */ + { 0x11,0x1B,0x15,0x11,0x11,0x11,0x11,0x00 }, /* M */ + { 0x09,0x0B,0x0D,0x09,0x09,0x09,0x09,0x00 }, /* N */ + { 0x06,0x09,0x09,0x09,0x09,0x09,0x06,0x00 }, /* O */ + { 0x07,0x09,0x09,0x07,0x01,0x01,0x01,0x00 }, /* P */ + { 0x06,0x09,0x09,0x09,0x09,0x05,0x0E,0x00 }, /* Q */ + { 0x07,0x09,0x09,0x07,0x09,0x09,0x09,0x00 }, /* R */ + { 0x06,0x09,0x01,0x06,0x08,0x09,0x06,0x00 }, /* S */ + { 0x07,0x02,0x02,0x02,0x02,0x02,0x02,0x00 }, /* T */ + { 0x09,0x09,0x09,0x09,0x09,0x09,0x06,0x00 }, /* U */ + { 0x11,0x11,0x11,0x11,0x11,0x0A,0x04,0x00 }, /* V */ + { 0x11,0x11,0x11,0x11,0x15,0x1B,0x11,0x00 }, /* W */ + { 0x11,0x11,0x0A,0x04,0x0A,0x11,0x11,0x00 }, /* X */ + { 0x11,0x11,0x0A,0x04,0x04,0x04,0x04,0x00 }, /* Y */ + { 0x0F,0x08,0x04,0x02,0x01,0x01,0x0F,0x00 }, /* Z */ + { 0x07,0x01,0x01,0x01,0x01,0x01,0x07,0x00 }, /* [ */ + { 0x01,0x01,0x02,0x02,0x04,0x04,0x08,0x00 }, /* \ */ + { 0x07,0x04,0x04,0x04,0x04,0x04,0x07,0x00 }, /* ] */ + { 0x04,0x0A,0x11,0x00,0x00,0x00,0x00,0x00 }, /* ^ */ + { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x1F }, /* _ */ + { 0x01,0x01,0x02,0x00,0x00,0x00,0x00,0x00 }, /* ` */ + { 0x00,0x00,0x0E,0x09,0x09,0x0D,0x0B,0x00 }, /* a */ + { 0x01,0x01,0x07,0x09,0x09,0x09,0x07,0x00 }, /* b */ + { 0x00,0x00,0x06,0x09,0x01,0x09,0x06,0x00 }, /* c */ + { 0x08,0x08,0x0E,0x09,0x09,0x09,0x0E,0x00 }, /* d */ + { 0x00,0x00,0x06,0x09,0x0F,0x01,0x0E,0x00 }, /* e */ + { 0x06,0x01,0x07,0x01,0x01,0x01,0x01,0x00 }, /* f */ + { 0x00,0x00,0x0E,0x09,0x09,0x0E,0x08,0x07 }, /* g */ + { 0x01,0x01,0x07,0x09,0x09,0x09,0x09,0x00 }, /* h */ + { 0x01,0x00,0x01,0x01,0x01,0x01,0x01,0x00 }, /* i */ + { 0x08,0x00,0x08,0x08,0x08,0x08,0x09,0x06 }, /* j */ + { 0x01,0x01,0x09,0x05,0x03,0x05,0x09,0x00 }, /* k */ + { 0x01,0x01,0x01,0x01,0x01,0x01,0x02,0x00 }, /* l */ + { 0x00,0x00,0x0B,0x15,0x15,0x11,0x11,0x00 }, /* m */ + { 0x00,0x00,0x07,0x09,0x09,0x09,0x09,0x00 }, /* n */ + { 0x00,0x00,0x06,0x09,0x09,0x09,0x06,0x00 }, /* o */ + { 0x00,0x00,0x07,0x09,0x09,0x07,0x01,0x01 }, /* p */ + { 0x00,0x00,0x0E,0x09,0x09,0x0E,0x08,0x08 }, /* q */ + { 0x00,0x00,0x05,0x03,0x01,0x01,0x01,0x00 }, /* r */ + { 0x00,0x00,0x0E,0x01,0x06,0x08,0x07,0x00 }, /* s */ + { 0x02,0x02,0x07,0x02,0x02,0x02,0x02,0x00 }, /* t */ + { 0x00,0x00,0x09,0x09,0x09,0x09,0x0E,0x00 }, /* u */ + { 0x00,0x00,0x09,0x09,0x09,0x05,0x03,0x00 }, /* v */ + { 0x00,0x00,0x11,0x11,0x15,0x15,0x1A,0x00 }, /* w */ + { 0x00,0x00,0x05,0x05,0x02,0x05,0x05,0x00 }, /* x */ + { 0x00,0x00,0x09,0x09,0x09,0x0E,0x08,0x07 }, /* y */ + { 0x00,0x00,0x0F,0x08,0x04,0x02,0x0F,0x00 }, /* z */ + { 0x04,0x02,0x02,0x01,0x02,0x02,0x04,0x00 }, /* { */ + { 0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01 }, /* | */ + { 0x01,0x02,0x02,0x04,0x02,0x02,0x01,0x00 }, /* } */ + { 0x00,0x00,0x26,0x19,0x00,0x00,0x00,0x00 }, /* ~ */ +}; + + +void SysFonts_Register(const cc_string* path) { } + +const cc_string* SysFonts_UNSAFE_GetDefault(void) { return &String_Empty; } + +void SysFonts_GetNames(struct StringsBuffer* buffer) { } + +cc_result SysFont_Make(struct FontDesc* desc, const cc_string* fontName, int size, int flags) { + desc->size = size; + desc->flags = flags; + desc->height = Drawer2D_AdjHeight(size); + + desc->handle = (void*)1; + return 0; +} + +void SysFont_MakeDefault(struct FontDesc* desc, int size, int flags) { + SysFont_Make(desc, NULL, size, flags); +} + +void SysFont_Free(struct FontDesc* desc) { +} + +static int CellWidth(cc_uint8* rows) { + int y, widest = 0; + + for (y = 0; y < CELL_SIZE; y++) + { + widest = max(widest, rows[y]); + } + return Math_Log2(widest) + 2; /* add padding for next character */ +} + +int SysFont_TextWidth(struct DrawTextArgs* args) { + int width = 0; + cc_string left = args->text, part; + char colorCode = 'f'; + int i; + + while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode)) + { + for (i = 0; i < part.length; i++) + { + cc_uint8 c = part.buffer[i]; + if (c == ' ') { + width += SPACE_WIDTH; + } else { + width += CellWidth(SysFont_GetRows(c)); + } + } + } + + width = max(1, width); + if (args->useShadow) width++; + return width; +} + +static void DrawCell(struct Bitmap* bmp, int x, int y, cc_uint8* rows, BitmapCol color) { + int srcX, srcY, dstX, dstY; + cc_uint8 row; + + for (srcY = 0; srcY < CELL_SIZE; srcY++) + { + dstY = y + srcY; + if (dstY < 0 || dstY >= bmp->height) continue; + + row = rows[srcY]; + for (srcX = 0; srcX < CELL_SIZE; srcX++) + { + dstX = x + srcX; + if (dstX < 0 || dstX >= bmp->width) continue; + + if (row & (1 << srcX)) { + Bitmap_GetPixel(bmp, dstX, dstY) = color; + } + } + } +} + +void SysFont_DrawText(struct DrawTextArgs* args, struct Bitmap* bmp, int x, int y, cc_bool shadow) { + cc_string left = args->text, part; + char colorCode = 'f'; + cc_uint8* rows; + BitmapCol color; + int i; + + if (shadow) { x++; y++; } + + while (Drawer2D_UNSAFE_NextPart(&left, &part, &colorCode)) + { + color = Drawer2D_GetColor(colorCode); + if (shadow) color = GetShadowColor(color); + + for (i = 0; i < part.length; i++) + { + cc_uint8 c = part.buffer[i]; + if (c == ' ') { + x += SPACE_WIDTH; + } else { + rows = SysFont_GetRows(c); + + DrawCell(bmp, x, y, rows, color); + x += CellWidth(rows); + } + } + } +} #endif diff --git a/third_party/gldc/Makefile b/third_party/gldc/Makefile index e0949b7b9..6539f0739 100644 --- a/third_party/gldc/Makefile +++ b/third_party/gldc/Makefile @@ -3,12 +3,10 @@ SOURCE_DIRS := src src/yalloc C_FILES := $(foreach dir,$(SOURCE_DIRS),$(wildcard $(dir)/*.c)) OBJS := $(notdir $(C_FILES:%.c=%.o)) -C_FLAGS = -O3 -DNDEBUG -mfsrra -mfsca -ffp-contract=fast -ffast-math -O3 -mpretend-cmove -fexpensive-optimizations -fomit-frame-pointer -finline-functions -flto -fno-fat-lto-objects -ml -m4-single-only -ffunction-sections -fdata-sections -std=gnu99 +C_FLAGS = -O3 -DNDEBUG -mfsrra -mfsca -ffp-contract=fast -ffast-math -O3 -mpretend-cmove -fexpensive-optimizations -fomit-frame-pointer -finline-functions -flto -fno-fat-lto-objects -ml -m4-single-only -ffunction-sections -fdata-sections -std=gnu99 C_DEFINES = -DDREAMCAST -DNDEBUG -D__DREAMCAST__ -D__arch_dreamcast -D_arch_dreamcast -D_arch_sub_pristine -C_INCLUDES = -I/opt/toolchains/dc/kos/include -I/opt/toolchains/dc/kos/kernel/arch/dreamcast/include -I/opt/toolchains/dc/kos/addons/include - TARGET := libGLdc.a ifeq ($(strip $(KOS_BASE)),) @@ -18,11 +16,11 @@ endif default: $(TARGET) %.o: src/%.c - kos-cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -c $< -o $@ + kos-cc $(C_DEFINES) $(C_FLAGS) -c $< -o $@ %.o: src/yalloc/%.c - kos-cc $(C_DEFINES) $(C_INCLUDES) $(C_FLAGS) -c $< -o $@ + kos-cc $(C_DEFINES) $(C_FLAGS) -c $< -o $@ $(TARGET): $(OBJS) kos-ar cr $@ $^ - kos-ranlib $@ + kos-ranlib $@ \ No newline at end of file