font_*: fully parameterize source/destination bit depth

After implementing this, I realized I don't like the appearance of the
monochrome font as much as the antialiased font.
This commit is contained in:
Zack Buhman 2023-12-23 21:55:03 +08:00
parent ffbfcd9fd5
commit b484b5d4fe
8 changed files with 106 additions and 55 deletions

Binary file not shown.

Binary file not shown.

View File

@ -189,7 +189,9 @@ inline void inflate_character(const uint8_t * src, const uint8_t c)
} }
*/ */
twiddle::texture2<4>(&texture[offset / 4], temp, 8, 8, 0, 0); twiddle::texture2<4>(&texture[offset / 4], temp,
8,
8 * 8);
} }
void inflate_font(const uint8_t * src) void inflate_font(const uint8_t * src)

View File

@ -125,14 +125,16 @@ void init_texture_memory(const struct opb_size& opb_size)
); );
} }
void inflate_font(const uint32_t * src, const uint32_t size) void inflate_font(const uint32_t * src,
const uint32_t stride,
const uint32_t curve_end_ix)
{ {
auto mem = reinterpret_cast<volatile texture_memory_alloc *>(texture_memory64); auto mem = reinterpret_cast<volatile texture_memory_alloc *>(texture_memory64);
auto texture = reinterpret_cast<volatile uint32_t *>(mem->texture); auto texture = reinterpret_cast<volatile uint32_t *>(mem->texture);
for (uint32_t i = 0; i < (size / 4); i++) { twiddle::texture3<8, 8>(texture, reinterpret_cast<const uint8_t *>(src),
texture[i] = src[i]; stride,
} curve_end_ix);
} }
template <int C> template <int C>
@ -150,7 +152,6 @@ void palette_data()
| ((i >> 2) << 5) | ((i >> 2) << 5)
| ((i >> 3) << 0); | ((i >> 3) << 0);
} }
holly.PALETTE_RAM[255] = 0xffff;
} }
uint32_t _ta_parameter_buf[((32 * 10 * 17) + 32) / 4]; uint32_t _ta_parameter_buf[((32 * 10 * 17) + 32) / 4];
@ -167,6 +168,7 @@ void main()
serial::integer<uint32_t>(font->first_char_code); serial::integer<uint32_t>(font->first_char_code);
serial::integer<uint32_t>(font->glyph_count); serial::integer<uint32_t>(font->glyph_count);
serial::integer<uint32_t>(font->glyph_height); serial::integer<uint32_t>(font->glyph_height);
serial::integer<uint32_t>(font->texture_stride);
serial::integer<uint32_t>(font->texture_width); serial::integer<uint32_t>(font->texture_width);
serial::integer<uint32_t>(font->texture_height); serial::integer<uint32_t>(font->texture_height);
serial::character('\n'); serial::character('\n');
@ -174,8 +176,9 @@ void main()
serial::integer<uint32_t>(((uint32_t)texture) - ((uint32_t)font)); serial::integer<uint32_t>(((uint32_t)texture) - ((uint32_t)font));
*/ */
uint32_t texture_size = font->max_z_curve_ix + 1; inflate_font(texture,
inflate_font(texture, texture_size); font->texture_stride,
font->max_z_curve_ix);
palette_data<256>(); palette_data<256>();
// The address of `ta_parameter_buf` must be a multiple of 32 bytes. // The address of `ta_parameter_buf` must be a multiple of 32 bytes.

View File

@ -130,18 +130,16 @@ constexpr inline uint32_t b(uint32_t v, uint32_t n)
return ((v >> n) & 1) << (4 * n); return ((v >> n) & 1) << (4 * n);
} }
void inflate_font(const uint32_t * src, const uint32_t size) void inflate_font(const uint32_t * src,
const uint32_t stride,
const uint32_t curve_end_ix)
{ {
auto mem = reinterpret_cast<volatile texture_memory_alloc *>(texture_memory64); auto mem = reinterpret_cast<volatile texture_memory_alloc *>(texture_memory64);
auto texture = reinterpret_cast<volatile uint32_t *>(mem->texture); auto texture = reinterpret_cast<volatile uint32_t *>(mem->texture);
for (uint32_t i = 0; i < (size / 4); i++) { twiddle::texture3<4, 1>(texture, reinterpret_cast<const uint8_t *>(src),
uint32_t v = src[i]; stride,
texture[(i * 4) + 0] = b(v, 7 ) | b(v, 6 ) | b(v, 5 ) | b(v, 4 ) | b(v, 3 ) | b(v, 2 ) | b(v, 1 ) | b(v, 0 ); curve_end_ix);
texture[(i * 4) + 1] = b(v, 15) | b(v, 14) | b(v, 13) | b(v, 12) | b(v, 11) | b(v, 10) | b(v, 9 ) | b(v, 8 );
texture[(i * 4) + 2] = b(v, 23) | b(v, 22) | b(v, 21) | b(v, 20) | b(v, 19) | b(v, 18) | b(v, 17) | b(v, 16);
texture[(i * 4) + 3] = b(v, 31) | b(v, 30) | b(v, 29) | b(v, 28) | b(v, 27) | b(v, 26) | b(v, 25) | b(v, 24);
}
} }
template <int C> template <int C>
@ -188,8 +186,9 @@ void main()
serial::integer<uint32_t>(((uint32_t)texture) - ((uint32_t)font)); serial::integer<uint32_t>(((uint32_t)texture) - ((uint32_t)font));
*/ */
uint32_t texture_size = font->max_z_curve_ix + 1; inflate_font(texture,
inflate_font(texture, texture_size); font->texture_stride,
font->max_z_curve_ix);
palette_data_mono(); palette_data_mono();
// The address of `ta_parameter_buf` must be a multiple of 32 bytes. // The address of `ta_parameter_buf` must be a multiple of 32 bytes.

View File

@ -32,9 +32,12 @@ struct font {
uint32_t first_char_code; uint32_t first_char_code;
uint16_t glyph_count; uint16_t glyph_count;
uint16_t glyph_height; uint16_t glyph_height;
uint16_t texture_stride;
uint16_t texture_width; uint16_t texture_width;
uint16_t texture_height; uint16_t texture_height;
uint16_t _pad;
uint32_t texture_size;
uint32_t max_z_curve_ix; uint32_t max_z_curve_ix;
} __attribute__ ((packed)); } __attribute__ ((packed));
static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 4)); static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 6));

View File

@ -55,9 +55,11 @@ int32_t
load_outline_char(const FT_Face face, load_outline_char(const FT_Face face,
const FT_Int32 load_flags, const FT_Int32 load_flags,
const FT_Render_Mode render_mode, const FT_Render_Mode render_mode,
const uint32_t bits_per_pixel,
const FT_ULong char_code, const FT_ULong char_code,
glyph * glyph, glyph * glyph,
uint8_t * texture, uint8_t * texture,
uint32_t texture_width,
struct rect& rect) struct rect& rect)
{ {
FT_Error error; FT_Error error;
@ -87,9 +89,16 @@ load_outline_char(const FT_Face face,
assert(face->glyph->bitmap.width == rect.width); assert(face->glyph->bitmap.width == rect.width);
assert(face->glyph->bitmap.rows == rect.height); assert(face->glyph->bitmap.rows == rect.height);
assert(bits_per_pixel == 8 || bits_per_pixel == 4 || bits_per_pixel == 2 || bits_per_pixel == 1);
const uint32_t pixels_per_byte = 8 / bits_per_pixel;
const uint32_t texture_stride = texture_width / pixels_per_byte;
std::cerr << "pixels per byte: " << pixels_per_byte << '\n';
std::cerr << "texture stride: " << texture_stride << '\n';
for (uint32_t y = 0; y < rect.height; y++) { for (uint32_t y = 0; y < rect.height; y++) {
for (uint32_t x = 0; x < rect.width; x++) { for (uint32_t x = 0; x < rect.width; x++) {
uint32_t texture_ix = (rect.y + y) * max_texture_dim + (rect.x + x); const uint32_t texture_ix = (rect.y + y) * texture_stride + (rect.x + x) / pixels_per_byte;
const uint32_t texture_ix_mod = (rect.x + x) % pixels_per_byte;
assert(texture_ix < max_texture_size); assert(texture_ix < max_texture_size);
uint8_t level; uint8_t level;
@ -108,12 +117,13 @@ load_outline_char(const FT_Face face,
assert(face->glyph->bitmap.num_grays == 256); assert(face->glyph->bitmap.num_grays == 256);
//std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n'; //std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n';
level = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; level = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x];
level >>= (8 - bits_per_pixel);
break; break;
default: default:
assert(false); assert(false);
break; break;
} }
texture[texture_ix] = level; texture[texture_ix] |= level << (bits_per_pixel * texture_ix_mod);
} }
} }
@ -154,8 +164,6 @@ load_all_positions(const FT_Face face,
const uint32_t num_glyphs = (end - start) + 1; const uint32_t num_glyphs = (end - start) + 1;
struct rect rects[num_glyphs]; struct rect rects[num_glyphs];
uint8_t temp[max_texture_size];
FT_Int32 load_flags; FT_Int32 load_flags;
FT_Render_Mode render_mode; FT_Render_Mode render_mode;
if (monochrome) { if (monochrome) {
@ -177,36 +185,23 @@ load_all_positions(const FT_Face face,
// calculate a 2-dimensional packing for the rectangles // calculate a 2-dimensional packing for the rectangles
auto window_curve_ix = pack_all(rects, num_glyphs); auto window_curve_ix = pack_all(rects, num_glyphs);
// render all of the glyps to a temporary buffer; const uint32_t bits_per_pixel = monochrome ? 1 : 8;
// render all of the glyphs to the texture;
for (uint32_t i = 0; i < num_glyphs; i++) { for (uint32_t i = 0; i < num_glyphs; i++) {
const uint32_t char_code = rects[i].char_code; const uint32_t char_code = rects[i].char_code;
int32_t err = load_outline_char(face, int32_t err = load_outline_char(face,
load_flags, load_flags,
render_mode, render_mode,
bits_per_pixel,
char_code, char_code,
&glyphs[char_code - start], &glyphs[char_code - start],
temp, reinterpret_cast<uint8_t *>(texture),
window_curve_ix.window.width,
rects[i]); rects[i]);
if (err < 0) assert(false); if (err < 0) assert(false);
} }
// twiddle the temporary buffer to become the final texture
if (monochrome) {
twiddle::texture2<1>(texture, temp,
window_curve_ix.window.width,
window_curve_ix.window.height,
max_texture_dim);
} else {
twiddle::texture2<8>(texture, temp,
window_curve_ix.window.width,
window_curve_ix.window.height,
max_texture_dim);
}
if (monochrome) {
window_curve_ix.max_z_curve_ix = window_curve_ix.max_z_curve_ix / 8;
}
return window_curve_ix; return window_curve_ix;
} }
@ -280,18 +275,32 @@ int main(int argc, char *argv[])
auto window_curve_ix = load_all_positions(face, monochrome, start, end, glyphs, texture); auto window_curve_ix = load_all_positions(face, monochrome, start, end, glyphs, texture);
uint32_t texture_stride;
uint32_t texture_size;
if (monochrome) {
texture_stride = window_curve_ix.window.width / 8;
texture_size = byteswap((window_curve_ix.max_z_curve_ix / 8) + 1);
} else {
texture_stride = window_curve_ix.window.width;
texture_size = byteswap((window_curve_ix.max_z_curve_ix / 1) + 1);
}
font font; font font;
font.first_char_code = byteswap(start); font.first_char_code = byteswap(start);
font.glyph_count = byteswap(num_glyphs); font.glyph_count = byteswap(num_glyphs);
font.glyph_height = byteswap(face->size->metrics.height); font.glyph_height = byteswap(face->size->metrics.height);
font.texture_stride = byteswap(texture_stride);
font.texture_width = byteswap(window_curve_ix.window.width); font.texture_width = byteswap(window_curve_ix.window.width);
font.texture_height = byteswap(window_curve_ix.window.height); font.texture_height = byteswap(window_curve_ix.window.height);
font.texture_size = byteswap(texture_size);
font.max_z_curve_ix = byteswap(window_curve_ix.max_z_curve_ix); font.max_z_curve_ix = byteswap(window_curve_ix.max_z_curve_ix);
std::cerr << "start: 0x" << std::hex << start << '\n'; std::cerr << "start: 0x" << std::hex << start << '\n';
std::cerr << "end: 0x" << std::hex << end << '\n'; std::cerr << "end: 0x" << std::hex << end << '\n';
std::cerr << "texture_width: " << std::dec << window_curve_ix.window.width << '\n'; std::cerr << "texture_stride: " << std::dec << texture_stride << '\n';
std::cerr << "texture_width: " << std::dec << window_curve_ix.window.width << '\n';
std::cerr << "texture_height: " << std::dec << window_curve_ix.window.height << '\n'; std::cerr << "texture_height: " << std::dec << window_curve_ix.window.height << '\n';
std::cerr << "texture_size: " << std::dec << texture_size << '\n';
std::cerr << "max_z_curve_ix: " << std::dec << window_curve_ix.max_z_curve_ix << '\n'; std::cerr << "max_z_curve_ix: " << std::dec << window_curve_ix.max_z_curve_ix << '\n';
FILE * out = fopen(argv[output_file_path], "w"); FILE * out = fopen(argv[output_file_path], "w");
@ -302,7 +311,7 @@ int main(int argc, char *argv[])
fwrite(reinterpret_cast<void*>(&font), (sizeof (font)), 1, out); fwrite(reinterpret_cast<void*>(&font), (sizeof (font)), 1, out);
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out); fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out);
fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), window_curve_ix.max_z_curve_ix + 1, out); fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
fclose(out); fclose(out);
} }

View File

@ -137,28 +137,63 @@ void texture_4bpp(volatile T * dst, const T * src, const uint32_t width, const u
} }
} }
template <int B, typename T, typename U> template <uint32_t dst_bits_per_pixel, typename T, typename U>
void texture2(volatile T * dst, const U * src, void texture2(volatile T * dst, const U * src,
const uint32_t width, const uint32_t height, const uint32_t src_stride,
const uint32_t stride) const uint32_t curve_end_ix)
{ {
constexpr uint32_t t_bits = (sizeof (T)) * 8; constexpr uint32_t t_bits = (sizeof (T)) * 8;
constexpr uint32_t bits_per_pixel = B; static_assert(t_bits >= dst_bits_per_pixel);
static_assert(t_bits >= bits_per_pixel); static_assert((t_bits / dst_bits_per_pixel) * dst_bits_per_pixel == t_bits);
static_assert((t_bits / bits_per_pixel) * bits_per_pixel == t_bits); constexpr uint32_t pixels_per_t = t_bits / dst_bits_per_pixel;
constexpr uint32_t pixels_per_t = t_bits / bits_per_pixel;
static_assert(pixels_per_t == 1 || pixels_per_t == 2 || pixels_per_t == 4 || pixels_per_t == 8 || pixels_per_t == 16 || pixels_per_t == 32); static_assert(pixels_per_t == 1 || pixels_per_t == 2 || pixels_per_t == 4 || pixels_per_t == 8 || pixels_per_t == 16 || pixels_per_t == 32);
T dst_val = 0; T dst_val = 0;
const uint32_t end_ix = from_xy(width - 1, height - 1); for (uint32_t curve_ix = 0; curve_ix <= curve_end_ix; curve_ix++) {
for (uint32_t curve_ix = 0; curve_ix <= end_ix; curve_ix++) {
auto [x, y] = from_ix(curve_ix); auto [x, y] = from_ix(curve_ix);
const U src_val = src[y * stride + x]; const U src_val = src[y * src_stride + x];
if constexpr (pixels_per_t == 1) { if constexpr (pixels_per_t == 1) {
dst[curve_ix] = src_val; dst[curve_ix] = src_val;
} else { } else {
const uint32_t curve_ix_mod = curve_ix & (pixels_per_t - 1); const uint32_t curve_ix_mod = curve_ix & (pixels_per_t - 1);
dst_val |= src_val << (bits_per_pixel * curve_ix_mod); dst_val |= src_val << (dst_bits_per_pixel * curve_ix_mod);
if (curve_ix_mod == (pixels_per_t - 1)) {
dst[curve_ix / pixels_per_t] = dst_val;
dst_val = 0;
}
}
}
}
template <uint32_t dst_bits_per_pixel, uint32_t src_bits_per_pixel, typename T, typename U>
void texture3(volatile T * dst, const U * src,
const uint32_t src_stride,
const uint32_t curve_end_ix)
{
constexpr uint32_t t_bits = (sizeof (T)) * 8;
static_assert(t_bits >= dst_bits_per_pixel);
static_assert((t_bits / dst_bits_per_pixel) * dst_bits_per_pixel == t_bits);
constexpr uint32_t pixels_per_t = t_bits / dst_bits_per_pixel;
static_assert(pixels_per_t == 1 || pixels_per_t == 2 || pixels_per_t == 4 || pixels_per_t == 8 || pixels_per_t == 16 || pixels_per_t == 32);
constexpr uint32_t u_bits = (sizeof (U)) * 8;
static_assert(u_bits >= src_bits_per_pixel);
static_assert((u_bits / src_bits_per_pixel) * src_bits_per_pixel == u_bits);
constexpr uint32_t pixels_per_u = u_bits / src_bits_per_pixel;
static_assert(pixels_per_u == 1 || pixels_per_u == 2 || pixels_per_u == 4 || pixels_per_u == 8 || pixels_per_u == 16 || pixels_per_u == 32);
T dst_val = 0;
for (uint32_t curve_ix = 0; curve_ix <= curve_end_ix; curve_ix++) {
auto [x, y] = from_ix(curve_ix);
const uint32_t src_ix = y * src_stride + (x / pixels_per_u);
const uint32_t src_ix_mod = x & (pixels_per_u - 1);
const U src_val = (src[src_ix] >> (src_bits_per_pixel * src_ix_mod)) & ((1 << src_bits_per_pixel) - 1);
if constexpr (pixels_per_t == 1) {
dst[curve_ix] = src_val;
} else {
const uint32_t curve_ix_mod = curve_ix & (pixels_per_t - 1);
dst_val |= src_val << (dst_bits_per_pixel * curve_ix_mod);
if (curve_ix_mod == (pixels_per_t - 1)) { if (curve_ix_mod == (pixels_per_t - 1)) {
dst[curve_ix / pixels_per_t] = dst_val; dst[curve_ix / pixels_per_t] = dst_val;