dreamcast/tools/z_curve_test.py
Zack Buhman 8f0afc2868 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).
2023-12-22 23:54:39 +08:00

173 lines
4.8 KiB
Python

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