Add Chinese text rendering support
Extend the bitmap font pipeline with the Chinese Joker glyph subset and encode supported UTF-8 text for Tonc TTE. Move the affine map clear of the expanded font, add a compile-time VRAM overlap guard, and cover every custom glyph with host tests.
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
#include "chinese_text.h"
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define CHINESE_GLYPH_FIRST 128
|
||||
|
||||
static const uint32_t s_chinese_codepoints[] = {
|
||||
#define CHINESE_GLYPH(codepoint) codepoint,
|
||||
#include "def_chinese_glyph_table.h"
|
||||
#undef CHINESE_GLYPH
|
||||
};
|
||||
|
||||
_Static_assert(
|
||||
sizeof(s_chinese_codepoints) / sizeof(s_chinese_codepoints[0]) <= 256 - CHINESE_GLYPH_FIRST,
|
||||
"Chinese glyph table exceeds the single-byte encoding range"
|
||||
);
|
||||
|
||||
static uint32_t decode_utf8_codepoint(const unsigned char** cursor)
|
||||
{
|
||||
const unsigned char* text = *cursor;
|
||||
|
||||
if (text[0] < 0x80)
|
||||
{
|
||||
*cursor += 1;
|
||||
return text[0];
|
||||
}
|
||||
|
||||
if ((text[0] & 0xE0) == 0xC0 && (text[1] & 0xC0) == 0x80)
|
||||
{
|
||||
*cursor += 2;
|
||||
return ((uint32_t)(text[0] & 0x1F) << 6) | (text[1] & 0x3F);
|
||||
}
|
||||
|
||||
if ((text[0] & 0xF0) == 0xE0 && (text[1] & 0xC0) == 0x80 && (text[2] & 0xC0) == 0x80)
|
||||
{
|
||||
*cursor += 3;
|
||||
return ((uint32_t)(text[0] & 0x0F) << 12) | ((uint32_t)(text[1] & 0x3F) << 6) |
|
||||
(text[2] & 0x3F);
|
||||
}
|
||||
|
||||
*cursor += 1;
|
||||
return '?';
|
||||
}
|
||||
|
||||
static uint32_t codepoint_to_glyph(uint32_t codepoint)
|
||||
{
|
||||
for (size_t i = 0; i < sizeof(s_chinese_codepoints) / sizeof(s_chinese_codepoints[0]); i++)
|
||||
{
|
||||
if (s_chinese_codepoints[i] == codepoint)
|
||||
{
|
||||
return CHINESE_GLYPH_FIRST + i;
|
||||
}
|
||||
}
|
||||
|
||||
return '?';
|
||||
}
|
||||
|
||||
static size_t encode_tte_codepoint(uint32_t codepoint, char* output, size_t output_size)
|
||||
{
|
||||
if (codepoint < 0x80)
|
||||
{
|
||||
if (output_size < 1)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
output[0] = (char)codepoint;
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (codepoint <= 0x7FF)
|
||||
{
|
||||
if (output_size < 2)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
output[0] = (char)(0xC0 | (codepoint >> 6));
|
||||
output[1] = (char)(0x80 | (codepoint & 0x3F));
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t chinese_text_encode(const char* utf8, char* output, size_t output_size)
|
||||
{
|
||||
if (utf8 == NULL || output == NULL || output_size == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
const unsigned char* cursor = (const unsigned char*)utf8;
|
||||
size_t output_len = 0;
|
||||
|
||||
while (*cursor != '\0')
|
||||
{
|
||||
uint32_t codepoint = decode_utf8_codepoint(&cursor);
|
||||
uint32_t tte_codepoint = codepoint < 0x80 ? codepoint : codepoint_to_glyph(codepoint);
|
||||
size_t encoded_size =
|
||||
encode_tte_codepoint(tte_codepoint, output + output_len, output_size - output_len - 1);
|
||||
|
||||
if (encoded_size == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
output_len += encoded_size;
|
||||
}
|
||||
|
||||
output[output_len] = '\0';
|
||||
return output_len;
|
||||
}
|
||||
@@ -25,6 +25,31 @@
|
||||
#include "soundbank.h"
|
||||
#include "soundbank_bin.h"
|
||||
|
||||
enum
|
||||
{
|
||||
TTE_ASCII_GLYPH_COUNT = 96,
|
||||
FONT_TILES_PER_ROW = 16,
|
||||
CHARBLOCK_4BPP_TILE_COUNT = 512,
|
||||
SCREENBLOCK_4BPP_TILE_COUNT = 64,
|
||||
CHINESE_GLYPH_COUNT = 0
|
||||
#define CHINESE_GLYPH(codepoint) +1
|
||||
#include "def_chinese_glyph_table.h"
|
||||
#undef CHINESE_GLYPH
|
||||
,
|
||||
TTE_FONT_TILE_COUNT =
|
||||
TTE_ASCII_GLYPH_COUNT +
|
||||
((CHINESE_GLYPH_COUNT + FONT_TILES_PER_ROW - 1) / FONT_TILES_PER_ROW) * FONT_TILES_PER_ROW,
|
||||
TTE_FONT_FIRST_TILE = TTE_CBB * CHARBLOCK_4BPP_TILE_COUNT,
|
||||
TTE_FONT_END_TILE = TTE_FONT_FIRST_TILE + TTE_FONT_TILE_COUNT,
|
||||
AFFINE_BG_MAP_FIRST_TILE = AFFINE_BG_SBB * SCREENBLOCK_4BPP_TILE_COUNT,
|
||||
AFFINE_BG_MAP_END_TILE = AFFINE_BG_MAP_FIRST_TILE + SCREENBLOCK_4BPP_TILE_COUNT,
|
||||
};
|
||||
|
||||
_Static_assert(
|
||||
TTE_FONT_END_TILE <= AFFINE_BG_MAP_FIRST_TILE || AFFINE_BG_MAP_END_TILE <= TTE_FONT_FIRST_TILE,
|
||||
"TTE font tiles overlap the affine background map"
|
||||
);
|
||||
|
||||
void init()
|
||||
{
|
||||
rng_init();
|
||||
|
||||
Reference in New Issue
Block a user