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).
This commit is contained in:
Zack Buhman 2023-12-22 23:54:39 +08:00
parent 3b7e1eaef8
commit 8f0afc2868
11 changed files with 145 additions and 133 deletions

1
.gitignore vendored
View File

@ -12,3 +12,4 @@ __pycache__
*.gch
scramble
cdi4dc
tools/ttf_outline

Binary file not shown.

View File

@ -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<global_sprite>() = sprite;
parameter.append<vertex_sprite_type_1>() =
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>() = 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<global_polygon_type_0>() = 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<float>(texture_width);
v = v / static_cast<float>(texture_height);
parameter.append<vertex_polygon_type_3>() =
vertex_polygon_type_3(x, y, z,
@ -183,16 +135,22 @@ void inflate_font(const uint32_t * src, const uint32_t size)
}
}
template <int C>
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)
// 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<const struct glyph *>(&font[1]);
auto texture = reinterpret_cast<const uint32_t *>(&glyphs[font->glyph_count]);
/*
serial::integer<uint32_t>(font->first_char_code);
serial::integer<uint32_t>(font->glyph_count);
serial::integer<uint32_t>(font->glyph_height);
@ -213,10 +172,11 @@ void main()
serial::character('\n');
serial::integer<uint32_t>(((uint32_t)glyphs) - ((uint32_t)font));
serial::integer<uint32_t>(((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>() = global_end_of_list();

View File

@ -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));

View File

@ -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;
}
}
}
}

View File

@ -1,5 +0,0 @@
#pragma once
#include <cstdint>
uint32_t pack_all(struct rect * rects, const uint32_t num_rects);

View File

@ -1,15 +1,16 @@
#include <cassert>
#include <cstdint>
#include <compare>
#include <iostream>
#include "insertion_sort.hpp"
#include "rect.hpp"
#include "../twiddle.hpp"
#include <iostream>
#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,7 +37,7 @@ 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],
uint32_t pack_into(uint8_t texture[max_texture.height][max_texture.width],
struct size& window,
struct rect& rect)
{
@ -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};
}

14
tools/2d_pack.hpp Normal file
View File

@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
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);

View File

@ -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:

View File

@ -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,7 +130,8 @@ enum {
argv_length = 7
};
void load_all_positions(const FT_Face face,
struct window_curve_ix
load_all_positions(const FT_Face face,
const uint32_t start,
const uint32_t end,
glyph * glyphs,
@ -137,7 +141,7 @@ void load_all_positions(const FT_Face face,
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 << "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<void*>(&font), (sizeof (font)), 1, out);
fwrite(reinterpret_cast<void*>(&glyphs[0]), (sizeof (glyph)), num_glyphs, out);
fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), texture_size, out);
fwrite(reinterpret_cast<void*>(&texture[0]), (sizeof (uint8_t)), window_curve_ix.max_z_curve_ix + 1, out);
fclose(out);
}