From 8f0afc2868bfa5d706ede712f4eb7b50f8271f23 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Fri, 22 Dec 2023 23:54:39 +0800 Subject: [PATCH] font_outline: remove all 1024/256/128 magic numbers This fully threads both the real minimum size of the texture and the dimensions of the texture through to the TA parameters. This also removes spurious zero-area drawing commands (space characters). --- .gitignore | 1 + dejavusansmono.data | Bin 34700 -> 34605 bytes example/font_outline.cpp | 112 +++++++-------------- font/font.hpp | 3 +- holly/isp_tsp.hpp | 28 ++++++ tools/2d-pack.hpp | 5 - tools/{2d-pack.cpp => 2d_pack.cpp} | 45 ++++----- tools/2d_pack.hpp | 14 +++ tools/Makefile | 6 +- tools/{ttf-outline.cpp => ttf_outline.cpp} | 64 ++++++------ tools/{z-curve-test.py => z_curve_test.py} | 0 11 files changed, 145 insertions(+), 133 deletions(-) delete mode 100644 tools/2d-pack.hpp rename tools/{2d-pack.cpp => 2d_pack.cpp} (69%) create mode 100644 tools/2d_pack.hpp rename tools/{ttf-outline.cpp => ttf_outline.cpp} (77%) rename tools/{z-curve-test.py => z_curve_test.py} (100%) diff --git a/.gitignore b/.gitignore index fbee810..b638a17 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,4 @@ __pycache__ *.gch scramble cdi4dc +tools/ttf_outline \ No newline at end of file diff --git a/dejavusansmono.data b/dejavusansmono.data index fff4ab5a59301fdbba93432f50663e4e609e74a6..fa1269856ff28f00d8fbfd551bf06fa87c32f9c1 100644 GIT binary patch delta 27 icmeC_XIk6GB%r{+z>vV;z}djSz&NL#fng($Y&!s2jRr9Q delta 27 icmZ46$JEo$#G}B#z>vV;z}djSz_?LBwtezMg=7F+f(E?+ diff --git a/example/font_outline.cpp b/example/font_outline.cpp index aa7ea42..e43e1a5 100644 --- a/example/font_outline.cpp +++ b/example/font_outline.cpp @@ -29,60 +29,6 @@ struct vertex { float v; }; -/* -// screen space coordinates -const struct vertex quad_verticies[4] = { - { 0.f, 64.f, 0.01f, 0.f, 1.f }, - { 0.f, 0.f, 0.01f, 0.f, 0.f }, - { 64.f, 0.f, 0.01f, 1.f, 0.f }, - { 64.f, 64.f, 0.01f, 1.f, 1.f, }, -}; - -uint32_t transform(uint32_t * ta_parameter_buf) -{ - auto parameter = ta_parameter_writer(ta_parameter_buf); - uint32_t texture_address = (offsetof (struct texture_memory_alloc, texture)); - constexpr uint32_t base_color = 0xffffffff; - auto sprite = global_sprite(base_color); - sprite.parameter_control_word = para_control::para_type::sprite - | para_control::list_type::opaque - | obj_control::col_type::packed_color - | obj_control::texture - | obj_control::_16bit_uv; - sprite.tsp_instruction_word = tsp_instruction_word::src_alpha_instr::one - | tsp_instruction_word::dst_alpha_instr::zero - | tsp_instruction_word::fog_control::no_fog - | tsp_instruction_word::texture_u_size::_8 // 8px - | tsp_instruction_word::texture_v_size::_8; // 8px - sprite.texture_control_word = texture_control_word::pixel_format::_565 - | texture_control_word::scan_order::twiddled - | texture_control_word::texture_address(texture_address / 8); - parameter.append() = sprite; - - parameter.append() = - vertex_sprite_type_1(quad_verticies[0].x, - quad_verticies[0].y, - quad_verticies[0].z, - quad_verticies[1].x, - quad_verticies[1].y, - quad_verticies[1].z, - quad_verticies[2].x, - quad_verticies[2].y, - quad_verticies[2].z, - quad_verticies[3].x, - quad_verticies[3].y, - uv_16bit(quad_verticies[0].u, quad_verticies[0].v), - uv_16bit(quad_verticies[1].u, quad_verticies[1].v), - uv_16bit(quad_verticies[2].u, quad_verticies[2].v)); - // curiously, there is no `dz` in vertex_sprite_type_1 - // curiously, there is no `du_dv` in vertex_sprite_type_1 - - parameter.append() = global_end_of_list(); - - return parameter.offset; -} -*/ - const struct vertex strip_vertices[4] = { // [ position ] [ uv coordinates ] { 0.f, 1.f, 0.f, 0.f, 1.f, }, @@ -93,7 +39,9 @@ const struct vertex strip_vertices[4] = { constexpr uint32_t strip_length = (sizeof (strip_vertices)) / (sizeof (struct vertex)); uint32_t transform(ta_parameter_writer& parameter, - const uint32_t first_char_code, const glyph * glyphs, + const uint32_t first_char_code, + const uint32_t texture_width, uint32_t texture_height, + const glyph * glyphs, const char * s, const uint32_t len, const uint32_t y_offset) { @@ -102,6 +50,13 @@ uint32_t transform(ta_parameter_writer& parameter, uint32_t advance = 0; // in 26.6 fixed-point for (uint32_t string_ix = 0; string_ix < len; string_ix++) { + char c = s[string_ix]; + auto& glyph = glyphs[c - first_char_code]; + if (glyph.bitmap.width == 0 || glyph.bitmap.height == 0) { + advance += glyph.metrics.horiAdvance; + continue; + } + auto polygon = global_polygon_type_0(texture_address); polygon.parameter_control_word = para_control::para_type::polygon_or_modifier_volume | para_control::list_type::opaque @@ -111,17 +66,14 @@ uint32_t transform(ta_parameter_writer& parameter, polygon.tsp_instruction_word = tsp_instruction_word::src_alpha_instr::one | tsp_instruction_word::dst_alpha_instr::zero | tsp_instruction_word::fog_control::no_fog - | tsp_instruction_word::texture_u_size::_128 - | tsp_instruction_word::texture_v_size::_256; + | tsp_instruction_word::texture_u_size::from_int(texture_width) + | tsp_instruction_word::texture_v_size::from_int(texture_height); polygon.texture_control_word = texture_control_word::pixel_format::_8bpp_palette | texture_control_word::scan_order::twiddled | texture_control_word::texture_address(texture_address / 8); parameter.append() = polygon; - char c = s[string_ix]; - auto& glyph = glyphs[c - first_char_code]; - for (uint32_t i = 0; i < strip_length; i++) { bool end_of_strip = i == strip_length - 1; @@ -142,8 +94,8 @@ uint32_t transform(ta_parameter_writer& parameter, v *= glyph.bitmap.height; u += glyph.bitmap.x; v += glyph.bitmap.y; - u = u / 128.f; - v = v / 256.f; + u = u / static_cast(texture_width); + v = v / static_cast(texture_height); parameter.append() = vertex_polygon_type_3(x, y, z, @@ -183,16 +135,22 @@ void inflate_font(const uint32_t * src, const uint32_t size) } } +template void palette_data() { + static_assert(C >= 2); + constexpr int increment = 256 / C; + holly.PAL_RAM_CTRL = pal_ram_ctrl::pixel_format::rgb565; - // palette of 256 greys - for (int i = 0; i < 256; i++) { - holly.PALETTE_RAM[i] = ((i >> 3) << 11) - | ((i >> 2) << 5) - | ((i >> 3) << 0); + // generate a palette with `C` shades of grey, + // ranging in intensity from rgb565(0, 0, 0) to rgb565(31, 63, 31) + for (int i = 0; i < 256; i += increment) { + holly.PALETTE_RAM[i / increment] = ((i >> 3) << 11) + | ((i >> 2) << 5) + | ((i >> 3) << 0); } + holly.PALETTE_RAM[255] = 0xffff; } uint32_t _ta_parameter_buf[((32 * 10 * 17) + 32) / 4]; @@ -205,6 +163,7 @@ void main() auto glyphs = reinterpret_cast(&font[1]); auto texture = reinterpret_cast(&glyphs[font->glyph_count]); + /* serial::integer(font->first_char_code); serial::integer(font->glyph_count); serial::integer(font->glyph_height); @@ -213,10 +172,11 @@ void main() serial::character('\n'); serial::integer(((uint32_t)glyphs) - ((uint32_t)font)); serial::integer(((uint32_t)texture) - ((uint32_t)font)); + */ - uint32_t texture_size = font->texture_width * font->texture_height; + uint32_t texture_size = font->max_z_curve_ix + 1; inflate_font(texture, texture_size); - palette_data(); + palette_data<256>(); // The address of `ta_parameter_buf` must be a multiple of 32 bytes. // This is mandatory for ch2-dma to the ta fifo polygon converter. @@ -256,13 +216,19 @@ void main() auto parameter = ta_parameter_writer(ta_parameter_buf); - transform(parameter, font->first_char_code, glyphs, + transform(parameter, + font->first_char_code, + font->texture_width, font->texture_height, + glyphs, ana, 17, - 0); + font->glyph_height * 0); - transform(parameter, font->first_char_code, glyphs, + transform(parameter, + font->first_char_code, + font->texture_width, font->texture_height, + glyphs, cabal, 26, - font->glyph_height); + font->glyph_height * 1); parameter.append() = global_end_of_list(); diff --git a/font/font.hpp b/font/font.hpp index 27fb418..ca8a05a 100644 --- a/font/font.hpp +++ b/font/font.hpp @@ -34,6 +34,7 @@ struct font { uint16_t glyph_height; uint16_t texture_width; uint16_t texture_height; + uint32_t max_z_curve_ix; } __attribute__ ((packed)); -static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 3)); +static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 4)); diff --git a/holly/isp_tsp.hpp b/holly/isp_tsp.hpp index 4a468f3..e1d524a 100644 --- a/holly/isp_tsp.hpp +++ b/holly/isp_tsp.hpp @@ -109,6 +109,20 @@ namespace tsp_instruction_word { constexpr uint32_t _256 = 5 << 3; constexpr uint32_t _512 = 6 << 3; constexpr uint32_t _1024 = 7 << 3; + + constexpr uint32_t from_int(uint32_t n) { + switch (n) { + default: [[fallthrough]]; + case 8: return _8; + case 16: return _16; + case 32: return _32; + case 64: return _64; + case 128: return _128; + case 256: return _256; + case 512: return _512; + case 1024: return _1024; + } + } } namespace texture_v_size { @@ -120,6 +134,20 @@ namespace tsp_instruction_word { constexpr uint32_t _256 = 5 << 0; constexpr uint32_t _512 = 6 << 0; constexpr uint32_t _1024 = 7 << 0; + + constexpr uint32_t from_int(uint32_t n) { + switch (n) { + default: [[fallthrough]]; + case 8: return _8; + case 16: return _16; + case 32: return _32; + case 64: return _64; + case 128: return _128; + case 256: return _256; + case 512: return _512; + case 1024: return _1024; + } + } } } diff --git a/tools/2d-pack.hpp b/tools/2d-pack.hpp deleted file mode 100644 index 33f3ea4..0000000 --- a/tools/2d-pack.hpp +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -uint32_t pack_all(struct rect * rects, const uint32_t num_rects); diff --git a/tools/2d-pack.cpp b/tools/2d_pack.cpp similarity index 69% rename from tools/2d-pack.cpp rename to tools/2d_pack.cpp index 1b6edd8..25a4583 100644 --- a/tools/2d-pack.cpp +++ b/tools/2d_pack.cpp @@ -1,15 +1,16 @@ #include #include #include +#include #include "insertion_sort.hpp" #include "rect.hpp" #include "../twiddle.hpp" -#include +#include "2d_pack.hpp" struct size { - uint32_t width; - uint32_t height; + uint16_t width; + uint16_t height; }; constexpr struct size max_texture = {1024, 1024}; @@ -36,9 +37,9 @@ inline bool area_valid(const uint8_t texture[max_texture.height][max_texture.wid return true; } -bool pack_into(uint8_t texture[max_texture.height][max_texture.width], - struct size& window, - struct rect& rect) +uint32_t pack_into(uint8_t texture[max_texture.height][max_texture.width], + struct size& window, + struct rect& rect) { uint32_t z_curve_ix = 0; @@ -52,7 +53,7 @@ bool pack_into(uint8_t texture[max_texture.height][max_texture.width], auto [x_offset, y_offset] = twiddle::from_ix(z_curve_ix); if (x_offset >= window.width and y_offset >= window.height) { - std::cerr << z_curve_ix << ' ' << window.width << ' ' << window.height << '\n'; + //std::cerr << z_curve_ix << ' ' << window.width << ' ' << window.height << '\n'; assert(window.width < max_texture.width || window.height < max_texture.height); if (window.width == window.height) { window.height *= 2; } else { window.width *= 2; } @@ -76,15 +77,19 @@ bool pack_into(uint8_t texture[max_texture.height][max_texture.width], rect.x = x_offset; rect.y = y_offset; - return true; + return twiddle::from_xy(rect.x + rect.width - 1, + rect.y + rect.height - 1); } else { z_curve_ix += 1; continue; } } + + assert(false); } -uint32_t pack_all(struct rect * rects, const uint32_t num_rects) +struct window_curve_ix +pack_all(struct rect * rects, const uint32_t num_rects) { uint8_t texture[max_texture.height][max_texture.width] = { 0 }; size window = {1, 1}; @@ -92,22 +97,16 @@ uint32_t pack_all(struct rect * rects, const uint32_t num_rects) // sort all rectangles by size insertion_sort(rects, num_rects); - uint32_t max_x = 0; - uint32_t max_y = 0; + uint32_t max_z_curve_ix = 0; for (uint32_t i = 0; i < num_rects; i++) { - std::cerr << "pack " << i << '\n'; - bool packed = pack_into(texture, window, rects[i]); - if (packed) { - const uint32_t x = rects[i].x + rects[i].width - 1; - const uint32_t y = rects[i].y + rects[i].height - 1; - if (x > max_x) max_x = x; - if (y > max_y) max_y = y; - } + uint32_t z_curve_ix = pack_into(texture, window, rects[i]); + //std::cerr << "z_curve_ix " << z_curve_ix << '\n'; + if (z_curve_ix > max_z_curve_ix) + max_z_curve_ix = z_curve_ix; } - const uint32_t curve_ix = twiddle::from_xy(max_x, max_y); - std::cerr << "max xy " << max_x << ' ' << max_y << '\n'; - std::cerr << "curve_ix " << curve_ix << '\n'; - return curve_ix; + std::cerr << "window size: " << window.width << ' ' << window.height << '\n'; + std::cerr << "max_z_curve_ix: " << max_z_curve_ix << '\n'; + return {window.width, window.height, max_z_curve_ix}; } diff --git a/tools/2d_pack.hpp b/tools/2d_pack.hpp new file mode 100644 index 0000000..679d62e --- /dev/null +++ b/tools/2d_pack.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +struct window_curve_ix { + struct { + uint16_t width; + uint16_t height; + } window; + uint32_t max_z_curve_ix; +}; + +struct window_curve_ix +pack_all(struct rect * rects, const uint32_t num_rects); diff --git a/tools/Makefile b/tools/Makefile index 0f38c97..216e912 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -4,7 +4,7 @@ CXXFLAGS = -std=c++23 CFLAGS += $(shell pkg-config --cflags freetype2) LDFLAGS = $(shell pkg-config --libs freetype2) -all: ttf-outline +all: ttf_outline %.o: %.cpp $(CXX) $(CFLAGS) $(CXXFLAGS) -c $< -o $@ @@ -12,10 +12,10 @@ all: ttf-outline %: %.o $(CXX) $(LDFLAGS) $^ -o $@ -ttf-outline: ttf-outline.o 2d-pack.o +ttf_outline: ttf_outline.o 2d_pack.o clean: - rm -f *.o ttf-convert ttf-bitmap + rm -f *.o ttf_outline .SUFFIXES: .INTERMEDIATE: diff --git a/tools/ttf-outline.cpp b/tools/ttf_outline.cpp similarity index 77% rename from tools/ttf-outline.cpp rename to tools/ttf_outline.cpp index 11740e0..46499cc 100644 --- a/tools/ttf-outline.cpp +++ b/tools/ttf_outline.cpp @@ -10,11 +10,14 @@ #include "../font/font.hpp" #include "rect.hpp" -#include "2d-pack.hpp" +#include "2d_pack.hpp" #include "../twiddle.hpp" std::endian _target_endian; +constexpr uint32_t max_texture_dim = 1024; +constexpr uint32_t max_texture_size = max_texture_dim * max_texture_dim; + uint32_t byteswap(const uint32_t n) { if (std::endian::native != _target_endian) { @@ -63,7 +66,7 @@ load_outline_char(const FT_Face face, return -1; } - std::cerr << "size " << face->glyph->bitmap.rows << ' ' << face->glyph->bitmap.width << '\n'; + //std::cerr << "size " << face->glyph->bitmap.rows << ' ' << face->glyph->bitmap.width << '\n'; //assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); @@ -81,19 +84,19 @@ load_outline_char(const FT_Face face, assert(face->glyph->bitmap.width == rect.width); assert(face->glyph->bitmap.rows == rect.height); - std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n'; + //std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n'; switch (face->glyph->bitmap.num_grays) { case 2: assert(false); break; case 256: - std::cerr << "rxy " << rect.x << ' ' << rect.y << '\n'; - std::cerr << "rwh " << rect.width << ' ' << rect.height << '\n'; + //std::cerr << "rxy " << rect.x << ' ' << rect.y << '\n'; + //std::cerr << "rwh " << rect.width << ' ' << rect.height << '\n'; for (uint32_t y = 0; y < rect.height; y++) { for (uint32_t x = 0; x < rect.width; x++) { - uint32_t texture_ix = (rect.y + y) * 1024 + (rect.x + x); - assert(texture_ix < 1024 * 1024); + uint32_t texture_ix = (rect.y + y) * max_texture_dim + (rect.x + x); + assert(texture_ix < max_texture_size); texture[texture_ix] = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; } } @@ -127,17 +130,18 @@ enum { argv_length = 7 }; -void load_all_positions(const FT_Face face, - const uint32_t start, - const uint32_t end, - glyph * glyphs, - uint32_t * texture - ) +struct window_curve_ix +load_all_positions(const FT_Face face, + const uint32_t start, + const uint32_t end, + glyph * glyphs, + uint32_t * texture + ) { const uint32_t num_glyphs = (end - start) + 1; struct rect rects[num_glyphs]; - uint8_t temp[1024 * 1024]; + uint8_t temp[max_texture_size]; // first, load all rectangles for (uint32_t char_code = start; char_code <= end; char_code++) { @@ -145,7 +149,7 @@ void load_all_positions(const FT_Face face, } // calculate a 2-dimensional packing for the rectangles - pack_all(rects, num_glyphs); + auto window_curve_ix = pack_all(rects, num_glyphs); // asdf for (uint32_t i = 0; i < num_glyphs; i++) { @@ -159,8 +163,11 @@ void load_all_positions(const FT_Face face, } twiddle::texture2<8>(texture, temp, - 128, 256, - 1024); + window_curve_ix.window.width, + window_curve_ix.window.height, + max_texture_dim); + + return window_curve_ix; } int main(int argc, char *argv[]) @@ -210,8 +217,6 @@ int main(int argc, char *argv[]) return -1; } - std::cerr << "here\n"; - uint32_t start; uint32_t end; @@ -224,20 +229,24 @@ int main(int argc, char *argv[]) uint32_t num_glyphs = (end - start) + 1; glyph glyphs[num_glyphs]; - uint32_t texture[(1024 * 1024) / 4]; - memset(texture, 0x00, 1024 * 1024); + uint32_t texture[max_texture_size / 4]; + memset(texture, 0x00, max_texture_size); + + auto window_curve_ix = load_all_positions(face, start, end, glyphs, texture); font font; font.first_char_code = byteswap(start); font.glyph_count = byteswap(num_glyphs); font.glyph_height = byteswap(face->size->metrics.height); - font.texture_width = 128; - font.texture_height = 256; - - load_all_positions(face, start, end, glyphs, texture); + font.texture_width = byteswap(window_curve_ix.window.width); + font.texture_height = byteswap(window_curve_ix.window.height); + font.max_z_curve_ix = byteswap(window_curve_ix.max_z_curve_ix); 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_height: " << std::dec << window_curve_ix.window.height << '\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"); if (out == NULL) { @@ -245,10 +254,9 @@ int main(int argc, char *argv[]) return -1; } - uint32_t texture_size = 128 * 256; fwrite(reinterpret_cast(&font), (sizeof (font)), 1, out); fwrite(reinterpret_cast(&glyphs[0]), (sizeof (glyph)), num_glyphs, out); - fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), texture_size, out); + fwrite(reinterpret_cast(&texture[0]), (sizeof (uint8_t)), window_curve_ix.max_z_curve_ix + 1, out); fclose(out); } diff --git a/tools/z-curve-test.py b/tools/z_curve_test.py similarity index 100% rename from tools/z-curve-test.py rename to tools/z_curve_test.py