example: implement font_outline
This still needs to be cleaned up, particularly to properly pass the texture size around--there are a few unnecessary '128x256' magic numbers scattered in the code.
This commit is contained in:
parent
a8cceee46e
commit
3b7e1eaef8
@ -12,7 +12,7 @@ AFLAGS = --fatal-warnings
|
|||||||
CARCH = -m4-single-only -ml
|
CARCH = -m4-single-only -ml
|
||||||
CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib
|
CFLAGS += -falign-functions=4 -ffunction-sections -fdata-sections -fshort-enums -ffreestanding -nostdlib
|
||||||
CFLAGS += -Wall -Werror -Wfatal-errors
|
CFLAGS += -Wall -Werror -Wfatal-errors
|
||||||
CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable
|
CFLAGS += -Wno-error=narrowing -Wno-error=unused-variable -Wno-error=array-bounds= -Wno-array-bounds
|
||||||
CFLAGS += -mfsca -funsafe-math-optimizations
|
CFLAGS += -mfsca -funsafe-math-optimizations
|
||||||
CFLAGS += -I$(dir $(MAKEFILE_PATH))
|
CFLAGS += -I$(dir $(MAKEFILE_PATH))
|
||||||
DEPFLAGS = -MMD -E
|
DEPFLAGS = -MMD -E
|
||||||
|
BIN
dejavusansmono.data
Normal file
BIN
dejavusansmono.data
Normal file
Binary file not shown.
5
dejavusansmono.hpp
Normal file
5
dejavusansmono.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
extern uint32_t _binary_dejavusansmono_data_start __asm("_binary_dejavusansmono_data_start");
|
||||||
|
extern uint32_t _binary_dejavusansmono_data_end __asm("_binary_dejavusansmono_data_end");
|
||||||
|
extern uint32_t _binary_dejavusansmono_data_size __asm("_binary_dejavusansmono_data_size");
|
@ -46,6 +46,19 @@ FONT_BITMAP_OBJ = \
|
|||||||
example/font_bitmap.elf: LDSCRIPT = $(LIB)/alt.lds
|
example/font_bitmap.elf: LDSCRIPT = $(LIB)/alt.lds
|
||||||
example/font_bitmap.elf: $(START_OBJ) $(FONT_BITMAP_OBJ)
|
example/font_bitmap.elf: $(START_OBJ) $(FONT_BITMAP_OBJ)
|
||||||
|
|
||||||
|
FONT_OUTLINE_OBJ = \
|
||||||
|
example/font_outline.o \
|
||||||
|
vga.o \
|
||||||
|
holly/core.o \
|
||||||
|
holly/region_array.o \
|
||||||
|
holly/background.o \
|
||||||
|
holly/ta_fifo_polygon_converter.o \
|
||||||
|
serial.o \
|
||||||
|
dejavusansmono.data.o
|
||||||
|
|
||||||
|
example/font_outline.elf: LDSCRIPT = $(LIB)/alt.lds
|
||||||
|
example/font_outline.elf: $(START_OBJ) $(FONT_OUTLINE_OBJ)
|
||||||
|
|
||||||
MACAW_MULTIPASS_OBJ = \
|
MACAW_MULTIPASS_OBJ = \
|
||||||
example/macaw_multipass.o \
|
example/macaw_multipass.o \
|
||||||
vga.o \
|
vga.o \
|
||||||
|
@ -189,7 +189,7 @@ inline void inflate_character(const uint8_t * src, const uint8_t c)
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
twiddle::texture2<4>(&texture[offset / 4], temp, 8, 8);
|
twiddle::texture2<4>(&texture[offset / 4], temp, 8, 8, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void inflate_font(const uint8_t * src)
|
void inflate_font(const uint8_t * src)
|
||||||
|
280
example/font_outline.cpp
Normal file
280
example/font_outline.cpp
Normal file
@ -0,0 +1,280 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#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.hpp"
|
||||||
|
|
||||||
|
#include "sperrypc.hpp"
|
||||||
|
|
||||||
|
struct vertex {
|
||||||
|
float x;
|
||||||
|
float y;
|
||||||
|
float z;
|
||||||
|
float u;
|
||||||
|
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, },
|
||||||
|
{ 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 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++) {
|
||||||
|
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::_128
|
||||||
|
| tsp_instruction_word::texture_v_size::_256;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 / 128.f;
|
||||||
|
v = v / 256.f;
|
||||||
|
|
||||||
|
parameter.append<vertex_polygon_type_3>() =
|
||||||
|
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<volatile texture_memory_alloc *>(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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void inflate_font(const uint32_t * src, const uint32_t size)
|
||||||
|
{
|
||||||
|
auto mem = reinterpret_cast<volatile texture_memory_alloc *>(texture_memory64);
|
||||||
|
auto texture = reinterpret_cast<volatile uint32_t *>(mem->texture);
|
||||||
|
|
||||||
|
for (uint32_t i = 0; i < (size / 4); i++) {
|
||||||
|
texture[i] = src[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void palette_data()
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t _ta_parameter_buf[((32 * 10 * 17) + 32) / 4];
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vga();
|
||||||
|
|
||||||
|
auto font = reinterpret_cast<const struct font *>(&_binary_dejavusansmono_data_start);
|
||||||
|
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);
|
||||||
|
serial::integer<uint32_t>(font->texture_width);
|
||||||
|
serial::integer<uint32_t>(font->texture_height);
|
||||||
|
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;
|
||||||
|
inflate_font(texture, texture_size);
|
||||||
|
palette_data();
|
||||||
|
|
||||||
|
// 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, glyphs,
|
||||||
|
ana, 17,
|
||||||
|
0);
|
||||||
|
|
||||||
|
transform(parameter, font->first_char_code, glyphs,
|
||||||
|
cabal, 26,
|
||||||
|
font->glyph_height);
|
||||||
|
|
||||||
|
parameter.append<global_end_of_list>() = 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++;
|
||||||
|
}
|
||||||
|
}
|
39
font/font.hpp
Normal file
39
font/font.hpp
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// this file is designed to be platform-agnostic
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// metrics are 26.6 fixed point
|
||||||
|
struct glyph_metrics {
|
||||||
|
int32_t horiBearingX;
|
||||||
|
int32_t horiBearingY;
|
||||||
|
int32_t horiAdvance;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
static_assert((sizeof (glyph_metrics)) == ((sizeof (int32_t)) * 3));
|
||||||
|
|
||||||
|
struct glyph_bitmap {
|
||||||
|
uint16_t x;
|
||||||
|
uint16_t y;
|
||||||
|
uint16_t width;
|
||||||
|
uint16_t height;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
static_assert((sizeof (glyph_bitmap)) == ((sizeof (uint16_t)) * 4));
|
||||||
|
|
||||||
|
struct glyph {
|
||||||
|
glyph_bitmap bitmap;
|
||||||
|
glyph_metrics metrics;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
static_assert((sizeof (glyph)) == ((sizeof (glyph_bitmap)) + (sizeof (glyph_metrics))));
|
||||||
|
|
||||||
|
struct font {
|
||||||
|
uint32_t first_char_code;
|
||||||
|
uint16_t glyph_count;
|
||||||
|
uint16_t glyph_height;
|
||||||
|
uint16_t texture_width;
|
||||||
|
uint16_t texture_height;
|
||||||
|
} __attribute__ ((packed));
|
||||||
|
|
||||||
|
static_assert((sizeof (font)) == ((sizeof (uint32_t)) * 3));
|
31
font/font_draw.cpp
Normal file
31
font/font_draw.cpp
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
|
||||||
|
uint32_t pixel_data(const uint32_t * dest, const uint8_t * glyph_bitmaps, const uint32_t size)
|
||||||
|
{
|
||||||
|
const uint8_t temp[size];
|
||||||
|
|
||||||
|
const uint32_t * buf = reinterpret_cast<const uint32_t *>(&glyph_bitmaps[0]);
|
||||||
|
|
||||||
|
copy<uint32_t>(table, buf, size);
|
||||||
|
|
||||||
|
return table_address;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t font_data(const uint32_t * buf, state& state)
|
||||||
|
{
|
||||||
|
constexpr uint32_t font_offset = 0;
|
||||||
|
constexpr uint32_t glyphs_offset = (sizeof (struct font));
|
||||||
|
const uint32_t glyph_bitmaps_offset = (sizeof (struct font)) + (sizeof (struct glyph)) * font->glyph_index;
|
||||||
|
|
||||||
|
auto font = reinterpret_cast<const font *>(&buf[font_offset / 4]);
|
||||||
|
auto glyphs = reinterpret_cast<const glyph *>(&buf[glyphs_offset / 4]);
|
||||||
|
auto glyph_bitmaps = &(reinterpret_cast<const uint8_t *>(buf))[glyph_bitmaps_offset];
|
||||||
|
|
||||||
|
for (uint32_t glyph_ix = 0; glyph_ix < font->glyph_index; glyph_ix++) {
|
||||||
|
auto& glyph_bitmap = glyphs[glyph_ix].bitmap;
|
||||||
|
|
||||||
|
auto bitmap = &glyph_bitmaps[glyph_bitmap.offset];
|
||||||
|
// bitmap.pitch may be zero; bitmap.pitch is a multiple of 8 pixels
|
||||||
|
SIZE__X(bitmap.pitch) | SIZE__Y(bitmap.rows);
|
||||||
|
}
|
||||||
|
}
|
113
tools/2d-pack.cpp
Normal file
113
tools/2d-pack.cpp
Normal file
@ -0,0 +1,113 @@
|
|||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <compare>
|
||||||
|
|
||||||
|
#include "insertion_sort.hpp"
|
||||||
|
#include "rect.hpp"
|
||||||
|
#include "../twiddle.hpp"
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
struct size {
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr struct size max_texture = {1024, 1024};
|
||||||
|
|
||||||
|
inline bool area_valid(const uint8_t texture[max_texture.height][max_texture.width],
|
||||||
|
const uint32_t x_offset,
|
||||||
|
const uint32_t y_offset,
|
||||||
|
const struct rect& rect,
|
||||||
|
const struct size& window)
|
||||||
|
{
|
||||||
|
for (uint32_t yi = 0; yi < rect.height; yi++) {
|
||||||
|
for (uint32_t xi = 0; xi < rect.width; xi++) {
|
||||||
|
uint32_t x = x_offset + xi;
|
||||||
|
uint32_t y = y_offset + yi;
|
||||||
|
|
||||||
|
if (texture[y][x] != 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (x >= window.width || y >= window.height)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool pack_into(uint8_t texture[max_texture.height][max_texture.width],
|
||||||
|
struct size& window,
|
||||||
|
struct rect& rect)
|
||||||
|
{
|
||||||
|
uint32_t z_curve_ix = 0;
|
||||||
|
|
||||||
|
if (rect.width == 0 || rect.height == 0) {
|
||||||
|
rect.x = 0;
|
||||||
|
rect.y = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (true) {
|
||||||
|
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';
|
||||||
|
assert(window.width < max_texture.width || window.height < max_texture.height);
|
||||||
|
if (window.width == window.height) { window.height *= 2; }
|
||||||
|
else { window.width *= 2; }
|
||||||
|
|
||||||
|
// when the window changes; start again from the beginning and
|
||||||
|
// re-check earlier locations that might have been skipped due
|
||||||
|
// to window size
|
||||||
|
z_curve_ix = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (area_valid(texture, x_offset, y_offset, rect, window)) {
|
||||||
|
for (uint32_t yi = 0; yi < rect.height; yi++) {
|
||||||
|
for (uint32_t xi = 0; xi < rect.width; xi++) {
|
||||||
|
uint32_t x = x_offset + xi;
|
||||||
|
uint32_t y = y_offset + yi;
|
||||||
|
|
||||||
|
texture[y][x] = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.x = x_offset;
|
||||||
|
rect.y = y_offset;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
z_curve_ix += 1;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t pack_all(struct rect * rects, const uint32_t num_rects)
|
||||||
|
{
|
||||||
|
uint8_t texture[max_texture.height][max_texture.width] = { 0 };
|
||||||
|
size window = {1, 1};
|
||||||
|
|
||||||
|
// sort all rectangles by size
|
||||||
|
insertion_sort(rects, num_rects);
|
||||||
|
|
||||||
|
uint32_t max_x = 0;
|
||||||
|
uint32_t max_y = 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
5
tools/2d-pack.hpp
Normal file
5
tools/2d-pack.hpp
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
uint32_t pack_all(struct rect * rects, const uint32_t num_rects);
|
23
tools/Makefile
Normal file
23
tools/Makefile
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
CFLAGS = -Og -g -gdwarf-4 -Wall -Wextra -Werror -Wfatal-errors -ggdb -Wno-error=unused-parameter -Wno-error=unused-variable -fstack-protector-strong
|
||||||
|
CXXFLAGS = -std=c++23
|
||||||
|
|
||||||
|
CFLAGS += $(shell pkg-config --cflags freetype2)
|
||||||
|
LDFLAGS = $(shell pkg-config --libs freetype2)
|
||||||
|
|
||||||
|
all: ttf-outline
|
||||||
|
|
||||||
|
%.o: %.cpp
|
||||||
|
$(CXX) $(CFLAGS) $(CXXFLAGS) -c $< -o $@
|
||||||
|
|
||||||
|
%: %.o
|
||||||
|
$(CXX) $(LDFLAGS) $^ -o $@
|
||||||
|
|
||||||
|
ttf-outline: ttf-outline.o 2d-pack.o
|
||||||
|
|
||||||
|
clean:
|
||||||
|
rm -f *.o ttf-convert ttf-bitmap
|
||||||
|
|
||||||
|
.SUFFIXES:
|
||||||
|
.INTERMEDIATE:
|
||||||
|
.SECONDARY:
|
||||||
|
.PHONY: all clean
|
17
tools/insertion_sort.hpp
Normal file
17
tools/insertion_sort.hpp
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
#include <cstdint>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
void insertion_sort(T * arr, int len)
|
||||||
|
{
|
||||||
|
int i = 1;
|
||||||
|
while (i < len) {
|
||||||
|
int j = i;
|
||||||
|
while (j > 0 && arr[j - 1] < arr[j]) {
|
||||||
|
std::swap(arr[j - 1], arr[j]);
|
||||||
|
j -= 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
}
|
16
tools/rect.hpp
Normal file
16
tools/rect.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
struct rect {
|
||||||
|
uint32_t char_code;
|
||||||
|
uint32_t width;
|
||||||
|
uint32_t height;
|
||||||
|
int32_t x;
|
||||||
|
int32_t y;
|
||||||
|
|
||||||
|
std::strong_ordering operator<=>(const rect& b) const
|
||||||
|
{
|
||||||
|
return (width * height) <=> (b.width * b.height);
|
||||||
|
}
|
||||||
|
};
|
98
tools/sizes.py
Normal file
98
tools/sizes.py
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
sizes = [
|
||||||
|
(0, 0),
|
||||||
|
(23, 4),
|
||||||
|
(9, 9),
|
||||||
|
(23, 20),
|
||||||
|
(30, 16),
|
||||||
|
(23, 19),
|
||||||
|
(23, 20),
|
||||||
|
(9, 3),
|
||||||
|
(29, 8),
|
||||||
|
(29, 8),
|
||||||
|
(14, 15),
|
||||||
|
(17, 17),
|
||||||
|
(9, 6),
|
||||||
|
(3, 9),
|
||||||
|
(5, 5),
|
||||||
|
(26, 16),
|
||||||
|
(23, 16),
|
||||||
|
(23, 15),
|
||||||
|
(23, 15),
|
||||||
|
(23, 15),
|
||||||
|
(23, 17),
|
||||||
|
(23, 15),
|
||||||
|
(23, 16),
|
||||||
|
(23, 15),
|
||||||
|
(23, 16),
|
||||||
|
(23, 17),
|
||||||
|
(17, 5),
|
||||||
|
(21, 6),
|
||||||
|
(16, 17),
|
||||||
|
(10, 17),
|
||||||
|
(16, 17),
|
||||||
|
(23, 14),
|
||||||
|
(27, 19),
|
||||||
|
(23, 19),
|
||||||
|
(23, 16),
|
||||||
|
(23, 15),
|
||||||
|
(23, 16),
|
||||||
|
(23, 15),
|
||||||
|
(23, 15),
|
||||||
|
(23, 17),
|
||||||
|
(23, 16),
|
||||||
|
(23, 14),
|
||||||
|
(23, 14),
|
||||||
|
(23, 18),
|
||||||
|
(23, 15),
|
||||||
|
(23, 17),
|
||||||
|
(23, 16),
|
||||||
|
(23, 17),
|
||||||
|
(23, 15),
|
||||||
|
(27, 17),
|
||||||
|
(23, 18),
|
||||||
|
(23, 16),
|
||||||
|
(23, 19),
|
||||||
|
(23, 15),
|
||||||
|
(23, 19),
|
||||||
|
(23, 20),
|
||||||
|
(23, 19),
|
||||||
|
(23, 19),
|
||||||
|
(23, 17),
|
||||||
|
(29, 7),
|
||||||
|
(26, 16),
|
||||||
|
(29, 8),
|
||||||
|
(9, 18),
|
||||||
|
(3, 20),
|
||||||
|
(6, 8),
|
||||||
|
(18, 15),
|
||||||
|
(24, 15),
|
||||||
|
(18, 14),
|
||||||
|
(24, 16),
|
||||||
|
(18, 17),
|
||||||
|
(24, 14),
|
||||||
|
(25, 16),
|
||||||
|
(24, 14),
|
||||||
|
(24, 16),
|
||||||
|
(31, 11),
|
||||||
|
(24, 16),
|
||||||
|
(24, 15),
|
||||||
|
(18, 17),
|
||||||
|
(18, 14),
|
||||||
|
(18, 16),
|
||||||
|
(25, 16),
|
||||||
|
(25, 15),
|
||||||
|
(18, 14),
|
||||||
|
(18, 14),
|
||||||
|
(23, 15),
|
||||||
|
(18, 14),
|
||||||
|
(18, 17),
|
||||||
|
(18, 20),
|
||||||
|
(18, 18),
|
||||||
|
(25, 18),
|
||||||
|
(18, 14),
|
||||||
|
(30, 13),
|
||||||
|
(32, 3),
|
||||||
|
(30, 13),
|
||||||
|
(5, 17),
|
||||||
|
(28, 17),
|
||||||
|
]
|
254
tools/ttf-outline.cpp
Normal file
254
tools/ttf-outline.cpp
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
#include <bit>
|
||||||
|
#include <sstream>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include <ft2build.h>
|
||||||
|
#include FT_FREETYPE_H
|
||||||
|
|
||||||
|
#include "../font/font.hpp"
|
||||||
|
#include "rect.hpp"
|
||||||
|
#include "2d-pack.hpp"
|
||||||
|
#include "../twiddle.hpp"
|
||||||
|
|
||||||
|
std::endian _target_endian;
|
||||||
|
|
||||||
|
uint32_t byteswap(const uint32_t n)
|
||||||
|
{
|
||||||
|
if (std::endian::native != _target_endian) {
|
||||||
|
return std::byteswap(n);
|
||||||
|
} else {
|
||||||
|
return n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
load_outline_char_bitmap_rect(const FT_Face face,
|
||||||
|
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);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
rect.char_code = char_code;
|
||||||
|
rect.height = face->glyph->bitmap.rows;
|
||||||
|
rect.width = face->glyph->bitmap.width;
|
||||||
|
rect.x = -1;
|
||||||
|
rect.y = -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t
|
||||||
|
load_outline_char(const FT_Face face,
|
||||||
|
const FT_ULong char_code,
|
||||||
|
glyph * glyph,
|
||||||
|
uint8_t * texture,
|
||||||
|
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);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Load_Glyph " << FT_Error_String(error) << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "size " << face->glyph->bitmap.rows << ' ' << face->glyph->bitmap.width << '\n';
|
||||||
|
|
||||||
|
//assert(face->glyph->format == FT_GLYPH_FORMAT_OUTLINE);
|
||||||
|
|
||||||
|
error = FT_Render_Glyph(face->glyph, FT_RENDER_MODE_NORMAL);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Render_Glyph " << FT_Error_String(error) << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(face->glyph->bitmap.pitch > 0)) {
|
||||||
|
assert(face->glyph->bitmap.width == 0);
|
||||||
|
assert(face->glyph->bitmap.rows == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
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) * 1024 + (rect.x + x);
|
||||||
|
assert(texture_ix < 1024 * 1024);
|
||||||
|
texture[texture_ix] = face->glyph->bitmap.buffer[y * face->glyph->bitmap.pitch + x];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(face->glyph->bitmap.num_grays == -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
glyph_bitmap& bitmap = glyph->bitmap;
|
||||||
|
bitmap.x = byteswap(rect.x);
|
||||||
|
bitmap.y = byteswap(rect.y);
|
||||||
|
bitmap.width = byteswap(rect.width);
|
||||||
|
bitmap.height = byteswap(rect.height);
|
||||||
|
|
||||||
|
glyph_metrics& metrics = glyph->metrics;
|
||||||
|
metrics.horiBearingX = byteswap(face->glyph->metrics.horiBearingX);
|
||||||
|
metrics.horiBearingY = byteswap(face->glyph->metrics.horiBearingY);
|
||||||
|
metrics.horiAdvance = byteswap(face->glyph->metrics.horiAdvance);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum {
|
||||||
|
start_hex = 1,
|
||||||
|
end_hex = 2,
|
||||||
|
pixel_size = 3,
|
||||||
|
target_endian = 4,
|
||||||
|
font_file_path = 5,
|
||||||
|
output_file_path = 6,
|
||||||
|
argv_length = 7
|
||||||
|
};
|
||||||
|
|
||||||
|
void 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];
|
||||||
|
|
||||||
|
// 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]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// calculate a 2-dimensional packing for the rectangles
|
||||||
|
pack_all(rects, num_glyphs);
|
||||||
|
|
||||||
|
// asdf
|
||||||
|
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,
|
||||||
|
char_code,
|
||||||
|
&glyphs[char_code - start],
|
||||||
|
temp,
|
||||||
|
rects[i]);
|
||||||
|
if (err < 0) assert(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
twiddle::texture2<8>(texture, temp,
|
||||||
|
128, 256,
|
||||||
|
1024);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
FT_Library library;
|
||||||
|
FT_Face face;
|
||||||
|
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";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_Init_FreeType(&library);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Init_FreeType\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
error = FT_New_Face(library, argv[font_file_path], 0, &face);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_New_Face\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::stringstream ss3;
|
||||||
|
int font_size;
|
||||||
|
ss3 << std::dec << argv[pixel_size];
|
||||||
|
ss3 >> font_size;
|
||||||
|
std::cerr << "font_size: " << font_size << '\n';
|
||||||
|
|
||||||
|
error = FT_Set_Pixel_Sizes(face, 0, font_size);
|
||||||
|
if (error) {
|
||||||
|
std::cerr << "FT_Set_Pixel_Sizes: " << FT_Error_String(error) << error << '\n';
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (std::string(argv[target_endian]).compare("little") == 0) {
|
||||||
|
_target_endian = std::endian::little;
|
||||||
|
} else if (std::string(argv[target_endian]).compare("big") == 0) {
|
||||||
|
_target_endian = std::endian::big;
|
||||||
|
} else {
|
||||||
|
std::cerr << "unknown endian: " << argv[target_endian] << '\n';
|
||||||
|
std::cerr << "expected one of: big, little\n";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "here\n";
|
||||||
|
|
||||||
|
uint32_t start;
|
||||||
|
uint32_t end;
|
||||||
|
|
||||||
|
std::stringstream ss1;
|
||||||
|
ss1 << std::hex << argv[start_hex];
|
||||||
|
ss1 >> start;
|
||||||
|
std::stringstream ss2;
|
||||||
|
ss2 << std::hex << argv[end_hex];
|
||||||
|
ss2 >> end;
|
||||||
|
|
||||||
|
uint32_t num_glyphs = (end - start) + 1;
|
||||||
|
glyph glyphs[num_glyphs];
|
||||||
|
uint32_t texture[(1024 * 1024) / 4];
|
||||||
|
memset(texture, 0x00, 1024 * 1024);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
std::cerr << "start: 0x" << std::hex << start << '\n';
|
||||||
|
std::cerr << "end: 0x" << std::hex << end << '\n';
|
||||||
|
|
||||||
|
FILE * out = fopen(argv[output_file_path], "w");
|
||||||
|
if (out == NULL) {
|
||||||
|
perror("fopen(w)");
|
||||||
|
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);
|
||||||
|
|
||||||
|
fclose(out);
|
||||||
|
}
|
172
tools/z-curve-test.py
Normal file
172
tools/z-curve-test.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
import sys
|
||||||
|
|
||||||
|
def from_xy(x: int, y: int) -> int:
|
||||||
|
# maximum texture size : 1024x1024
|
||||||
|
# maximum 1-dimensional index: 0xfffff
|
||||||
|
# bits : 19-0
|
||||||
|
|
||||||
|
twiddle_ix = 0
|
||||||
|
for i in range(0, (19 // 2) + 1):
|
||||||
|
twiddle_ix |= ((y >> i) & 1) << (i * 2 + 0)
|
||||||
|
twiddle_ix |= ((x >> i) & 1) << (i * 2 + 1)
|
||||||
|
|
||||||
|
return twiddle_ix
|
||||||
|
|
||||||
|
assert from_xy(0b000, 0b000) == 0
|
||||||
|
assert from_xy(0b001, 0b000) == 2
|
||||||
|
assert from_xy(0b010, 0b000) == 8
|
||||||
|
assert from_xy(0b011, 0b000) == 10
|
||||||
|
assert from_xy(0b100, 0b000) == 32
|
||||||
|
assert from_xy(0b101, 0b000) == 34
|
||||||
|
assert from_xy(0b110, 0b000) == 40
|
||||||
|
assert from_xy(0b111, 0b000) == 42
|
||||||
|
|
||||||
|
assert from_xy(0b000, 0b001) == 1
|
||||||
|
assert from_xy(0b000, 0b010) == 4
|
||||||
|
assert from_xy(0b000, 0b011) == 5
|
||||||
|
assert from_xy(0b000, 0b100) == 16
|
||||||
|
assert from_xy(0b000, 0b101) == 17
|
||||||
|
assert from_xy(0b000, 0b110) == 20
|
||||||
|
assert from_xy(0b000, 0b111) == 21
|
||||||
|
|
||||||
|
def from_ix(z_curve_ix: int) -> tuple[int, int]:
|
||||||
|
x_y = [0, 0]
|
||||||
|
xyi = 0
|
||||||
|
while z_curve_ix != 0:
|
||||||
|
x_y[(xyi + 1) % 2] |= (z_curve_ix & 1) << (xyi // 2)
|
||||||
|
z_curve_ix >>= 1
|
||||||
|
xyi += 1
|
||||||
|
return tuple(x_y)
|
||||||
|
|
||||||
|
assert from_ix(17) == (0b000, 0b101)
|
||||||
|
assert from_ix(21) == (0b000, 0b111)
|
||||||
|
assert from_ix(42) == (0b111, 0b000)
|
||||||
|
|
||||||
|
"""
|
||||||
|
def texture(src: list[int],
|
||||||
|
width: int, height: int) -> list[int]:
|
||||||
|
dst = [0] * (width * height)
|
||||||
|
|
||||||
|
for y in range(0, height):
|
||||||
|
for x in range(0, width):
|
||||||
|
twiddle_ix = from_xy(x, y)
|
||||||
|
value = src[y * width + x]
|
||||||
|
dst[twiddle_ix] = value
|
||||||
|
|
||||||
|
return dst
|
||||||
|
"""
|
||||||
|
|
||||||
|
import random
|
||||||
|
from sizes import sizes as _sizes
|
||||||
|
from colorsys import hsv_to_rgb
|
||||||
|
|
||||||
|
def all_colors(num):
|
||||||
|
for i in range(num):
|
||||||
|
hue = i / (num - 1)
|
||||||
|
rgb = hsv_to_rgb(hue, 1.0, 1.0)
|
||||||
|
def color():
|
||||||
|
for i in rgb:
|
||||||
|
yield int(i * 255)
|
||||||
|
yield tuple(color())
|
||||||
|
|
||||||
|
def random_colors(num):
|
||||||
|
l = list(all_colors(num))
|
||||||
|
random.shuffle(l)
|
||||||
|
return l
|
||||||
|
|
||||||
|
def area_pixels(x_off, y_off, width, height):
|
||||||
|
for x in range(height):
|
||||||
|
for y in range(width):
|
||||||
|
px_ix = (x_off + x, y_off + y)
|
||||||
|
yield px_ix
|
||||||
|
|
||||||
|
max_size = (1, 1)
|
||||||
|
|
||||||
|
def pack_into(texture: dict[tuple[int, int], int],
|
||||||
|
width_height: tuple[int, int],
|
||||||
|
z_curve_ix: int):
|
||||||
|
global max_size
|
||||||
|
# ignore passed z_curve_ix
|
||||||
|
z_curve_ix = 0
|
||||||
|
|
||||||
|
width, height = width_height
|
||||||
|
if width == 0 or height == 0:
|
||||||
|
return (0, 0), z_curve_ix
|
||||||
|
|
||||||
|
while True:
|
||||||
|
x_off, y_off = from_ix(z_curve_ix)
|
||||||
|
if x_off >= max_size[0] and y_off >= max_size[0]:
|
||||||
|
if max_size[0] == max_size[1]:
|
||||||
|
max_size = (max_size[0], max_size[1] * 2)
|
||||||
|
else:
|
||||||
|
max_size = (max_size[0] * 2, max_size[1])
|
||||||
|
z_curve_ix = 0
|
||||||
|
|
||||||
|
if all((pixel not in texture) and (pixel[0] < max_size[0] and pixel[1] < max_size[1])
|
||||||
|
for pixel in area_pixels(x_off, y_off, width, height)):
|
||||||
|
#x, y = x_off + width - 1, y_off + height - 1
|
||||||
|
return (x_off, y_off), z_curve_ix
|
||||||
|
else:
|
||||||
|
z_curve_ix += 1
|
||||||
|
|
||||||
|
def sort_by_area(width_height):
|
||||||
|
width, height = width_height
|
||||||
|
area = width * height
|
||||||
|
return area
|
||||||
|
|
||||||
|
max_ix = 0
|
||||||
|
|
||||||
|
def insert_into_texture(texture, color_ix, x_off__y_off, width_height):
|
||||||
|
global max_ix
|
||||||
|
x_off, y_off = x_off__y_off
|
||||||
|
width, height = width_height
|
||||||
|
for px_ix in area_pixels(x_off, y_off, width, height):
|
||||||
|
assert px_ix not in texture, px_ix
|
||||||
|
ix = from_xy(*px_ix)
|
||||||
|
if ix > max_ix:
|
||||||
|
max_ix = ix
|
||||||
|
texture[px_ix] = color_ix
|
||||||
|
|
||||||
|
def pack_all(sizes):
|
||||||
|
global max_size
|
||||||
|
max_size = (1, 1)
|
||||||
|
global max_ix
|
||||||
|
max_ix = 0
|
||||||
|
|
||||||
|
sorted_sizes = sorted(sizes, key=sort_by_area, reverse=True)
|
||||||
|
z_curve_ix = 0
|
||||||
|
texture = dict()
|
||||||
|
for color_ix, width_height in enumerate(sorted_sizes):
|
||||||
|
x_off__y_off, z_curve_ix = pack_into(texture, width_height, z_curve_ix)
|
||||||
|
insert_into_texture(texture, color_ix, x_off__y_off, width_height)
|
||||||
|
#if color_ix == 2:
|
||||||
|
# break
|
||||||
|
return texture
|
||||||
|
|
||||||
|
def ppm(texture: dict, num_colors):
|
||||||
|
colors = random_colors(num_colors)
|
||||||
|
max_x = max(px[0] for px in texture.keys())
|
||||||
|
max_y = max(px[1] for px in texture.keys())
|
||||||
|
width = max_x + 1
|
||||||
|
height = max_y + 1
|
||||||
|
print(" max xy:", max_x, max_y, file=sys.stderr)
|
||||||
|
print("max curve ix:", from_xy(max_x, max_y), file=sys.stderr)
|
||||||
|
|
||||||
|
yield "P3"
|
||||||
|
yield f"{width} {height}"
|
||||||
|
yield "255"
|
||||||
|
for y in range(0, height):
|
||||||
|
for x in range(0, width):
|
||||||
|
if (x, y) in texture:
|
||||||
|
i = texture[(x, y)]
|
||||||
|
color = colors[i]
|
||||||
|
yield " ".join(map(str, color))
|
||||||
|
else:
|
||||||
|
yield "0 0 0"
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
texture = pack_all((x, y) for (y, x) in _sizes)
|
||||||
|
print("ideal size", sum(x * y for x, y in _sizes), file=sys.stderr)
|
||||||
|
image = list(ppm(texture, len(_sizes)))
|
||||||
|
print("\n".join(image))
|
||||||
|
pass
|
40
twiddle.hpp
40
twiddle.hpp
@ -93,22 +93,23 @@ from_ix(uint32_t curve_ix)
|
|||||||
return {x, y};
|
return {x, y};
|
||||||
}
|
}
|
||||||
|
|
||||||
static_assert(from_ix(0) == std::tuple<uint32_t, uint32_t>{0b000, 0b000});
|
using xy_type = std::tuple<uint32_t, uint32_t>;
|
||||||
static_assert(from_ix(2) == std::tuple<uint32_t, uint32_t>{0b001, 0b000});
|
static_assert(from_ix(0) == xy_type{0b000, 0b000});
|
||||||
static_assert(from_ix(8) == std::tuple<uint32_t, uint32_t>{0b010, 0b000});
|
static_assert(from_ix(2) == xy_type{0b001, 0b000});
|
||||||
static_assert(from_ix(10) == std::tuple<uint32_t, uint32_t>{0b011, 0b000});
|
static_assert(from_ix(8) == xy_type{0b010, 0b000});
|
||||||
static_assert(from_ix(32) == std::tuple<uint32_t, uint32_t>{0b100, 0b000});
|
static_assert(from_ix(10) == xy_type{0b011, 0b000});
|
||||||
static_assert(from_ix(34) == std::tuple<uint32_t, uint32_t>{0b101, 0b000});
|
static_assert(from_ix(32) == xy_type{0b100, 0b000});
|
||||||
static_assert(from_ix(40) == std::tuple<uint32_t, uint32_t>{0b110, 0b000});
|
static_assert(from_ix(34) == xy_type{0b101, 0b000});
|
||||||
static_assert(from_ix(42) == std::tuple<uint32_t, uint32_t>{0b111, 0b000});
|
static_assert(from_ix(40) == xy_type{0b110, 0b000});
|
||||||
|
static_assert(from_ix(42) == xy_type{0b111, 0b000});
|
||||||
|
|
||||||
static_assert(from_ix(1) == std::tuple<uint32_t, uint32_t>{0b000, 0b001});
|
static_assert(from_ix(1) == xy_type{0b000, 0b001});
|
||||||
static_assert(from_ix(4) == std::tuple<uint32_t, uint32_t>{0b000, 0b010});
|
static_assert(from_ix(4) == xy_type{0b000, 0b010});
|
||||||
static_assert(from_ix(5) == std::tuple<uint32_t, uint32_t>{0b000, 0b011});
|
static_assert(from_ix(5) == xy_type{0b000, 0b011});
|
||||||
static_assert(from_ix(16) == std::tuple<uint32_t, uint32_t>{0b000, 0b100});
|
static_assert(from_ix(16) == xy_type{0b000, 0b100});
|
||||||
static_assert(from_ix(17) == std::tuple<uint32_t, uint32_t>{0b000, 0b101});
|
static_assert(from_ix(17) == xy_type{0b000, 0b101});
|
||||||
static_assert(from_ix(20) == std::tuple<uint32_t, uint32_t>{0b000, 0b110});
|
static_assert(from_ix(20) == xy_type{0b000, 0b110});
|
||||||
static_assert(from_ix(21) == std::tuple<uint32_t, uint32_t>{0b000, 0b111});
|
static_assert(from_ix(21) == xy_type{0b000, 0b111});
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void texture(volatile T * dst, const T * src, const uint32_t width, const uint32_t height)
|
void texture(volatile T * dst, const T * src, const uint32_t width, const uint32_t height)
|
||||||
@ -137,7 +138,9 @@ void texture_4bpp(volatile T * dst, const T * src, const uint32_t width, const u
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <int B, typename T, typename U>
|
template <int B, typename T, typename U>
|
||||||
void texture2(volatile T * dst, const U * src, const uint32_t width, const uint32_t height)
|
void texture2(volatile T * dst, const U * src,
|
||||||
|
const uint32_t width, const uint32_t height,
|
||||||
|
const uint32_t stride)
|
||||||
{
|
{
|
||||||
constexpr uint32_t t_bits = (sizeof (T)) * 8;
|
constexpr uint32_t t_bits = (sizeof (T)) * 8;
|
||||||
constexpr uint32_t bits_per_pixel = B;
|
constexpr uint32_t bits_per_pixel = B;
|
||||||
@ -147,9 +150,10 @@ void texture2(volatile T * dst, const U * src, const uint32_t width, const uint3
|
|||||||
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);
|
||||||
|
|
||||||
T dst_val = 0;
|
T dst_val = 0;
|
||||||
for (uint32_t curve_ix = 0; curve_ix < (width * height); curve_ix++) {
|
const uint32_t end_ix = from_xy(width - 1, height - 1);
|
||||||
|
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 * width + x];
|
const U src_val = src[y * 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 {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user