TR-core/tools: add font multiplier script

grdddj/debuglink_improvements
grdddj 1 year ago
parent f8afaf4039
commit 651300c513

@ -0,0 +1,153 @@
# Helpers to increase the font size from existing font data.
# Thanks to it we can save space by not having to include
# the larger font definitions.
from __future__ import annotations
from typing_extensions import Literal
from typing import Tuple
Bit = Literal[0, 1]
Point = Tuple[int, int]
def magnify_glyph_by_two(width: int, height: int, bytes_data: list[int]) -> list[int]:
"""Magnifying the font size by two.
Input and output are bytes (that is the standard in font files),
but internally works with bits.
"""
bits_data = _bytes_to_bits(bytes_data, height * width)
double_size_data = _double_the_bits(width, bits_data)
return _bits_to_bytes(double_size_data)
def _bytes_to_bits(bytes_data: list[int], bits_to_take: int) -> list[Bit]:
"""Transform bytes into bits."""
bits_data = [f"{i:08b}" for i in bytes_data[:-1]]
# Last element needs to be handled carefully,
# to respect the number of bits to take.
missing_bits = bits_to_take - len(bits_data) * 8
last_byte = bytes_data[-1]
last_binary = f"{last_byte:08b}"
# Taking either the right or left part of the last byte
if last_byte < 2**missing_bits:
bits_data.append(last_binary[-missing_bits:])
else:
bits_data.append(last_binary[:missing_bits])
return [1 if int(x) else 0 for x in "".join(bits_data)]
def _bits_to_bytes(bits_data: list[Bit]) -> list[int]:
"""Transform bits into bytes."""
bits_str = "".join([str(bit) for bit in bits_data])
bytes_str_list = [bits_str[i : i + 8] for i in range(0, len(bits_str), 8)]
# Last element needs to be right-padded to 8 bits
while len(bytes_str_list[-1]) != 8:
bytes_str_list[-1] += "0"
return [int(byte, 2) for byte in bytes_str_list]
def _double_the_bits(width: int, bits_data: list[Bit]) -> list[Bit]:
"""Double the dimension of a given glyph."""
# Allocate space for the new data - 2*2 bigger than the original
# Then fill all the new indexes with appropriate bits
double_size_data: list[Bit] = [0 for _ in range(4 * len(bits_data))]
for original_index, bit in enumerate(bits_data):
for new_index in _corresponding_indexes(original_index, width):
double_size_data[new_index] = bit
return double_size_data
def _corresponding_indexes(index: int, width: int) -> list[int]:
"""Find the indexes of the four pixels that correspond to the given one."""
point = _index_to_point(index, width)
points = _scale_by_two(point)
new_width = 2 * width
return [_point_to_index(new_point, new_width) for new_point in points]
def _scale_by_two(point: Point) -> tuple[Point, Point, Point, Point]:
"""Translate one pixel into four adjacent pixels to visually scale it by two."""
x, y = point
return (
(x * 2, y * 2),
(x * 2 + 1, y * 2),
(x * 2, y * 2 + 1),
(x * 2 + 1, y * 2 + 1),
)
def _point_to_index(point: Point, width: int) -> int:
"""Translate point to index according to the glyph width."""
x, y = point
assert 0 <= x < width
return y * width + x
def _index_to_point(index: int, width: int) -> Point:
"""Translate index to point according to the glyph width."""
x = index % width
y = index // width
return x, y
def print_from_bits(width: int, height: int, bit_data: list[Bit]):
"""Print the glyph into terminal from bit data."""
for line_id in range(height):
line = bit_data[line_id * width : (line_id + 1) * width]
str_line = "".join([str(x) for x in line])
print(str_line.replace("0", " "))
print()
def print_from_bytes(width: int, height: int, bytes_data: list[int]):
"""Print the glyph into terminal from byte data."""
bits_data = _bytes_to_bits(bytes_data, height * width)
print_from_bits(width, height, bits_data)
################################
# TEST SECTION for pytest
################################
# fmt: off
HEIGHT = 7
K_WIDTH = 5
K_GLYPH = [140, 169, 138, 74, 32]
K_BITS = [1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1]
K_MAGNIFIED = [192, 240, 60, 51, 12, 204, 51, 15, 3, 192, 204, 51, 12, 51, 12, 192, 240, 48]
M_GLYPH = [131, 7, 29, 89, 48, 96, 128]
M_WIDTH = 7
# fmt: on
def test_bits_to_bytes_and_back():
vectors = ( # height, width, bytes_data
(HEIGHT, K_WIDTH, K_GLYPH),
(HEIGHT, M_WIDTH, M_GLYPH),
)
for height, width, bytes_data in vectors:
bits_data = _bytes_to_bits(bytes_data, height * width)
assert _bits_to_bytes(bits_data) == bytes_data
def test_bit_to_bytes():
assert _bytes_to_bits(K_GLYPH, HEIGHT * K_WIDTH) == K_BITS
def test_overall_magnify():
assert magnify_glyph_by_two(K_WIDTH, HEIGHT, K_GLYPH) == K_MAGNIFIED
if __name__ == "__main__":
# Print letter K, magnify it and print it also
print("K_GLYPH", K_GLYPH)
print_from_bytes(K_WIDTH, HEIGHT, K_GLYPH)
magnified_data = magnify_glyph_by_two(K_WIDTH, HEIGHT, K_GLYPH)
print("magnified_data", magnified_data)
print_from_bytes(2 * K_WIDTH, 2 * HEIGHT, magnified_data)
Loading…
Cancel
Save