diff --git a/dejavusansmono_mono.data b/dejavusansmono_mono.data new file mode 100644 index 0000000..4d53cf8 Binary files /dev/null and b/dejavusansmono_mono.data differ diff --git a/dejavusansmono_mono.hpp b/dejavusansmono_mono.hpp new file mode 100644 index 0000000..ca93a73 --- /dev/null +++ b/dejavusansmono_mono.hpp @@ -0,0 +1,5 @@ +#include + +extern uint32_t _binary_dejavusansmono_mono_data_start __asm("_binary_dejavusansmono_mono_data_start"); +extern uint32_t _binary_dejavusansmono_mono_data_end __asm("_binary_dejavusansmono_mono_data_end"); +extern uint32_t _binary_dejavusansmono_mono_data_size __asm("_binary_dejavusansmono_mono_data_size"); diff --git a/example/example.mk b/example/example.mk index c3c942c..948292b 100644 --- a/example/example.mk +++ b/example/example.mk @@ -59,6 +59,19 @@ FONT_OUTLINE_OBJ = \ example/font_outline.elf: LDSCRIPT = $(LIB)/alt.lds example/font_outline.elf: $(START_OBJ) $(FONT_OUTLINE_OBJ) +FONT_OUTLINE_PUNCH_THROUGH_OBJ = \ + example/font_outline_punch_through.o \ + vga.o \ + holly/core.o \ + holly/region_array.o \ + holly/background.o \ + holly/ta_fifo_polygon_converter.o \ + serial.o \ + dejavusansmono_mono.data.o + +example/font_outline_punch_through.elf: LDSCRIPT = $(LIB)/alt.lds +example/font_outline_punch_through.elf: $(START_OBJ) $(FONT_OUTLINE_PUNCH_THROUGH_OBJ) + MACAW_MULTIPASS_OBJ = \ example/macaw_multipass.o \ vga.o \ diff --git a/example/font_outline_punch_through.cpp b/example/font_outline_punch_through.cpp new file mode 100644 index 0000000..2a5b65c --- /dev/null +++ b/example/font_outline_punch_through.cpp @@ -0,0 +1,260 @@ +#include + +#include "align.hpp" + +#include "vga.hpp" +#include "holly.hpp" +#include "holly/core.hpp" +#include "holly/core_bits.hpp" +#include "holly/ta_parameter.hpp" +#include "holly/ta_fifo_polygon_converter.hpp" +#include "holly/texture_memory_alloc.hpp" +#include "memorymap.hpp" +#include "holly/background.hpp" +#include "holly/region_array.hpp" +#include "holly/ta_bits.hpp" +#include "twiddle.hpp" +#include "serial.hpp" + +#include "font/font.hpp" +#include "dejavusansmono_mono.hpp" + +#include "sperrypc.hpp" + +struct vertex { + float x; + float y; + float z; + float u; + float v; +}; + +const struct vertex strip_vertices[4] = { + // [ position ] [ uv coordinates ] + { 0.f, 1.f, 0.f, 0.f, 1.f, }, + { 0.f, 0.f, 0.f, 0.f, 0.f, }, + { 1.f, 1.f, 0.f, 1.f, 1.f, }, + { 1.f, 0.f, 0.f, 1.f, 0.f, }, +}; +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 uint32_t texture_width, uint32_t texture_height, + const glyph * glyphs, + const char * s, const uint32_t len, + const uint32_t y_offset) +{ + uint32_t texture_address = (offsetof (struct texture_memory_alloc, texture)); + + 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 + | obj_control::col_type::packed_color + | obj_control::texture; + + 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::from_int(texture_width) + | tsp_instruction_word::texture_v_size::from_int(texture_height); + + polygon.texture_control_word = texture_control_word::pixel_format::_4bpp_palette + | texture_control_word::scan_order::twiddled + | texture_control_word::texture_address(texture_address / 8); + parameter.append() = polygon; + + for (uint32_t i = 0; i < strip_length; i++) { + bool end_of_strip = i == strip_length - 1; + + float x = strip_vertices[i].x; + float y = strip_vertices[i].y; + float z = strip_vertices[i].z; + + x *= glyph.bitmap.width; + y *= glyph.bitmap.height; + x += 100.f + ((advance + glyph.metrics.horiBearingX) >> 6); + y += 200.f - ((glyph.metrics.horiBearingY) >> 6); + y += y_offset >> 6; + z = 1.f / (z + 10.f); + + float u = strip_vertices[i].u; + float v = strip_vertices[i].v; + u *= glyph.bitmap.width; + v *= glyph.bitmap.height; + u += glyph.bitmap.x; + v += glyph.bitmap.y; + u = u / static_cast(texture_width); + v = v / static_cast(texture_height); + + parameter.append() = + vertex_polygon_type_3(x, y, z, + u, v, + 0x00000000, // base_color + end_of_strip); + } + + advance += glyph.metrics.horiAdvance; + } + + return parameter.offset; +} + + +void init_texture_memory(const struct opb_size& opb_size) +{ + auto mem = reinterpret_cast(texture_memory32); + + background_parameter(mem->background, 0xff0000ff); + + region_array2(mem->region_array, + (offsetof (struct texture_memory_alloc, object_list)), + 640 / 32, // width + 480 / 32, // height + opb_size + ); +} + +constexpr inline uint32_t b(uint32_t v, uint32_t n) +{ + return ((v >> n) & 1) << (4 * n); +} + +void inflate_font(const uint32_t * src, const uint32_t size) +{ + auto mem = reinterpret_cast(texture_memory64); + auto texture = reinterpret_cast(mem->texture); + + for (uint32_t i = 0; i < (size / 4); i++) { + uint32_t v = src[i]; + 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 ); + 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 +void palette_data() +{ + static_assert(C >= 2); + constexpr int increment = 256 / C; + + holly.PAL_RAM_CTRL = pal_ram_ctrl::pixel_format::rgb565; + + // 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); + } +} + +void palette_data_mono() +{ + holly.PALETTE_RAM[0] = 0; + holly.PALETTE_RAM[1] = 0xffff; +} + +uint32_t _ta_parameter_buf[((32 * 10 * 17) + 32) / 4]; + +void main() +{ + vga(); + + auto font = reinterpret_cast(&_binary_dejavusansmono_mono_data_start); + 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); + serial::integer(font->texture_width); + serial::integer(font->texture_height); + serial::character('\n'); + serial::integer(((uint32_t)glyphs) - ((uint32_t)font)); + serial::integer(((uint32_t)texture) - ((uint32_t)font)); + */ + + uint32_t texture_size = font->max_z_curve_ix + 1; + inflate_font(texture, texture_size); + palette_data_mono(); + + // 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. + uint32_t * ta_parameter_buf = align_32byte(_ta_parameter_buf); + + constexpr uint32_t ta_alloc = ta_alloc_ctrl::pt_opb::no_list + | ta_alloc_ctrl::tm_opb::no_list + | ta_alloc_ctrl::t_opb::no_list + | ta_alloc_ctrl::om_opb::no_list + | ta_alloc_ctrl::o_opb::_16x4byte; + + constexpr struct opb_size opb_size = { .opaque = 16 * 4 + , .opaque_modifier = 0 + , .translucent = 0 + , .translucent_modifier = 0 + , .punch_through = 0 + }; + + constexpr uint32_t tiles = (640 / 32) * (320 / 32); + + holly.SOFTRESET = softreset::pipeline_soft_reset + | softreset::ta_soft_reset; + holly.SOFTRESET = 0; + + core_init(); + init_texture_memory(opb_size); + + uint32_t frame_ix = 0; + constexpr uint32_t num_frames = 1; + + const char ana[18] = "A from ana i know"; + const char cabal[27] = "where is this secret cabal"; + + while (true) { + ta_polygon_converter_init(opb_size.total() * tiles, ta_alloc, + 640, 480); + + auto parameter = ta_parameter_writer(ta_parameter_buf); + + transform(parameter, + font->first_char_code, + font->texture_width, font->texture_height, + glyphs, + ana, 17, + font->glyph_height * 0); + + transform(parameter, + font->first_char_code, + font->texture_width, font->texture_height, + glyphs, + cabal, 26, + font->glyph_height * 1); + + parameter.append() = global_end_of_list(); + + ta_polygon_converter_transfer(ta_parameter_buf, parameter.offset); + ta_wait_opaque_list(); + + core_start_render(frame_ix, num_frames); + + v_sync_out(); + v_sync_in(); + core_wait_end_of_render_video(frame_ix, num_frames); + + frame_ix++; + } +} diff --git a/tools/2d_pack.cpp b/tools/2d_pack.cpp index 25a4583..57b512c 100644 --- a/tools/2d_pack.cpp +++ b/tools/2d_pack.cpp @@ -106,7 +106,7 @@ pack_all(struct rect * rects, const uint32_t num_rects) max_z_curve_ix = z_curve_ix; } - std::cerr << "window size: " << window.width << ' ' << window.height << '\n'; + //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/ttf_outline.cpp b/tools/ttf_outline.cpp index 46499cc..ceed772 100644 --- a/tools/ttf_outline.cpp +++ b/tools/ttf_outline.cpp @@ -29,13 +29,14 @@ uint32_t byteswap(const uint32_t n) int32_t load_outline_char_bitmap_rect(const FT_Face face, + const FT_Int32 load_flags, const FT_ULong char_code, struct rect& rect) { FT_Error error; FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); - error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph(face, glyph_index, load_flags); if (error) { std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n'; return -1; @@ -52,6 +53,8 @@ load_outline_char_bitmap_rect(const FT_Face face, int32_t load_outline_char(const FT_Face face, + const FT_Int32 load_flags, + const FT_Render_Mode render_mode, const FT_ULong char_code, glyph * glyph, uint8_t * texture, @@ -60,7 +63,7 @@ load_outline_char(const FT_Face face, FT_Error error; FT_UInt glyph_index = FT_Get_Char_Index(face, char_code); - error = FT_Load_Glyph(face, glyph_index, FT_LOAD_DEFAULT); + error = FT_Load_Glyph(face, glyph_index, load_flags); if (error) { std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n'; return -1; @@ -70,7 +73,7 @@ load_outline_char(const FT_Face face, //assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE); - error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL); + error = FT_Render_Glyph(face->glyph, render_mode); if (error) { std::cerr << "FT_Render_Glyph " << FT_Error_String(error) << '\n'; return -1; @@ -84,26 +87,34 @@ 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'; - 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'; + 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) * max_texture_dim + (rect.x + x); + assert(texture_ix < max_texture_size); - 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) * 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]; + uint8_t level; + + //std::cerr << "rxy " << rect.x << ' ' << rect.y << '\n'; + //std::cerr << "rwh " << rect.width << ' ' << rect.height << '\n'; + + //std::cerr << "pixel_mode " << (int)face->glyph->bitmap.pixel_mode << '\n'; + switch (face->glyph->bitmap.pixel_mode) { + case FT_PIXEL_MODE_MONO: + // [num_grays] is only used with FT_PIXEL_MODE_GRAY; it gives the number + // of gray levels used in the bitmap. + level = (face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + (x / 8)] >> (7 - (x % 8))) & 1; + break; + case FT_PIXEL_MODE_GRAY: + assert(face->glyph->bitmap.num_grays == 256); + //std::cerr << "num_grays " << face->glyph->bitmap.num_grays << '\n'; + level = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x]; + break; + default: + assert(false); + break; } + texture[texture_ix] = level; } - - break; - default: - assert(face->glyph->bitmap.num_grays == -1); } glyph_bitmap& bitmap = glyph->bitmap; @@ -124,14 +135,16 @@ enum { start_hex = 1, end_hex = 2, pixel_size = 3, - target_endian = 4, - font_file_path = 5, - output_file_path = 6, - argv_length = 7 + monochrome_out = 4, + target_endian = 5, + font_file_path = 6, + output_file_path = 7, + argv_length = 8 }; struct window_curve_ix load_all_positions(const FT_Face face, + bool monochrome, const uint32_t start, const uint32_t end, glyph * glyphs, @@ -143,18 +156,33 @@ load_all_positions(const FT_Face face, uint8_t temp[max_texture_size]; + FT_Int32 load_flags; + FT_Render_Mode render_mode; + if (monochrome) { + load_flags = FT_LOAD_MONOCHROME | FT_LOAD_TARGET_MONO; + render_mode = FT_RENDER_MODE_MONO; + } else { + load_flags = FT_LOAD_DEFAULT; + render_mode = FT_RENDER_MODE_NORMAL; + } + // first, load all rectangles for (uint32_t char_code = start; char_code <= end; char_code++) { - load_outline_char_bitmap_rect(face, char_code, rects[char_code - start]); + load_outline_char_bitmap_rect(face, + load_flags, + char_code, + rects[char_code - start]); } // calculate a 2-dimensional packing for the rectangles auto window_curve_ix = pack_all(rects, num_glyphs); - // asdf + // render all of the glyps to a temporary buffer; for (uint32_t i = 0; i < num_glyphs; i++) { const uint32_t char_code = rects[i].char_code; int32_t err = load_outline_char(face, + load_flags, + render_mode, char_code, &glyphs[char_code - start], temp, @@ -162,10 +190,22 @@ load_all_positions(const FT_Face face, if (err < 0) assert(false); } - twiddle::texture2<8>(texture, temp, - window_curve_ix.window.width, - window_curve_ix.window.height, - max_texture_dim); + // 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; } @@ -177,9 +217,9 @@ int main(int argc, char *argv[]) FT_Error error; if (argc != argv_length) { - std::cerr << "usage: " << argv[0] << " [start-hex] [end-hex] [pixel-size] [target-endian] [font-file-path] [output-file-path]\n\n"; - std::cerr << "ex. 1: " << argv[0] << " 3000 30ff 30 little ipagp.ttf font.bin\n"; - std::cerr << "ex. 2: " << argv[0] << " 20 7f 30 big DejaVuSans.ttf font.bin\n"; + std::cerr << "usage: " << argv[0] << " [start-hex] [end-hex] [pixel-size] [monochrome-out] [target-endian] [font-file-path] [output-file-path]\n\n"; + std::cerr << "ex. 1: " << argv[0] << " 3000 30ff 30 0 little ipagp.ttf font.bin\n"; + std::cerr << "ex. 2: " << argv[0] << " 20 7f 30 1 big DejaVuSans.ttf font.bin\n"; return -1; } @@ -200,6 +240,12 @@ int main(int argc, char *argv[]) ss3 << std::dec << argv[pixel_size]; ss3 >> font_size; std::cerr << "font_size: " << font_size << '\n'; + std::stringstream ss4; + int monochrome; + ss4 << std::dec << argv[monochrome_out]; + ss4 >> monochrome; + assert(monochrome == 0 || monochrome == 1); + std::cerr << "monochrome: " << monochrome << '\n'; error = FT_Set_Pixel_Sizes(face, 0, font_size); if (error) { @@ -232,7 +278,7 @@ int main(int argc, char *argv[]) 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); + auto window_curve_ix = load_all_positions(face, monochrome, start, end, glyphs, texture); font font; font.first_char_code = byteswap(start); diff --git a/twiddle.hpp b/twiddle.hpp index 9ccedd1..b46a22c 100644 --- a/twiddle.hpp +++ b/twiddle.hpp @@ -147,7 +147,7 @@ void texture2(volatile T * dst, const U * src, static_assert(t_bits >= bits_per_pixel); static_assert((t_bits / bits_per_pixel) * bits_per_pixel == t_bits); 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); + 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; const uint32_t end_ix = from_xy(width - 1, height - 1);