Add font loader

This commit is contained in:
Pyrex 2024-02-26 12:42:07 -08:00
parent e05f050e7d
commit cb44cab4f8
16 changed files with 1584 additions and 190 deletions

View File

@ -1,6 +1,24 @@
###############################################################################
# Bazel now uses Bzlmod by default to manage external dependencies.
# Please consider migrating your external dependencies from WORKSPACE to MODULE.bazel.
#
# For more details, please check https://github.com/bazelbuild/bazel/issues/18958
###############################################################################
"""
CrocParty: a tiny platformer.
"""
module(
name="crocparty",
version="0.0",
)
bazel_dep(name="rules_python", version="0.31.0")
# Set up Python
python = use_extension("@rules_python//python/extensions:python.bzl", "python")
python.toolchain(
python_version = "3.10",
is_default = True,
)
pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse(
hub_name = "pytools_deps",
python_version = "3.10",
requirements_lock = "//pytools:requirements.txt",
)
use_repo(pip, "pytools_deps")

1603
MODULE.bazel.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -4,9 +4,9 @@
#include <stdint.h>
#include <stdbool.h>
// 240 x 135 is also cool
#define DEVICE_W 128
#define DEVICE_H 128
// 256 x 144 is also cool
#define DEVICE_W 256
#define DEVICE_H 144
typedef enum {
DEVICE_BUTTON_L=0,

View File

@ -49,10 +49,10 @@ void game_update() {
ellipse_x0 += ellipse_dx0;
if (ellipse_x0 < 0 || ellipse_x0 > DEVICE_W * 4) { ellipse_dx0 *= -1; }
ellipse_y0 += ellipse_dy0;
if (ellipse_y0 < 0 || ellipse_y0 > DEVICE_W * 4) { ellipse_dy0 *= -1; }
if (ellipse_y0 < 0 || ellipse_y0 > DEVICE_H * 4) { ellipse_dy0 *= -1; }
ellipse_x1 += ellipse_dx1;
if (ellipse_x1 < 0 || ellipse_x1 > DEVICE_H * 4) { ellipse_dx1 *= -1; }
if (ellipse_x1 < 0 || ellipse_x1 > DEVICE_W * 4) { ellipse_dx1 *= -1; }
ellipse_y1 += ellipse_dy1;
if (ellipse_y1 < 0 || ellipse_y1 > DEVICE_H * 4) { ellipse_dy1 *= -1; }
}
@ -89,4 +89,6 @@ void game_draw() {
sys_circ_draw_ext(x0, y0, 6, 248, false);
sys_circ_draw_ext(x0, y0, 8, 244, false);
sys_line_draw(x0, y0, x1, y1, 254);
sys_print("Hello, blood\nsources!", x1, y1, 224);
}

1
pytools/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
venv/

10
pytools/BUILD Normal file
View File

@ -0,0 +1,10 @@
load("@pytools_deps//:requirements.bzl", "requirement")
py_binary(
name = "font",
srcs = ["font.py"],
deps = [
requirement("pillow"),
],
visibility = ["//visibility:public"],
)

53
pytools/font.py Normal file
View File

@ -0,0 +1,53 @@
from typing import Tuple
from PIL import Image
import sys
def main(font_name, n_glyphs, fname_png, fname_c):
glyphs = load_glyphs(fname_png)
assert(len(glyphs) == n_glyphs), f"must be exactly {n_glyphs} glyphs"
with open(fname_c, "wt") as output:
output.writelines([
"// generated code! be nice!\n",
"#include \"sys/sys.h\";\n",
f"sys_glyph {font_name}[{n_glyphs}] = {{{', '.join(str(g) for g in glyphs)}}};\n"
])
def load_glyphs(fname_png: str):
with Image.open(fname_png) as im:
width = im.width
height = im.height
assert width % 8 == 0, "width must be a multiple of 8"
assert height % 8 == 0, "height must be a multiple of 8"
data = list(im.convert("RGBA").getdata())
monochrome = [pixel_to_monochrome(p) for p in data]
glyphs = []
for gy in range(0, height, 8):
for gx in range(0, width, 8):
glyph = 0
for py in range(0, 8):
for px in range(0, 8):
x = gx + px
y = gy + py
if monochrome[y * width + x]:
glyph |= 1 << (py * 8 + px)
glyphs.append(glyph)
return glyphs
def pixel_to_monochrome(rgba: Tuple[int, int, int, int]):
if rgba[3] < 128: return False
if (rgba[0] + rgba[1] + rgba[2])/3 < 128: return False
return True
if __name__ == "__main__":
assert len(sys.argv) == 5, \
"there must be four args (font name, n glyphs, src png, out c)"
main(sys.argv[1], int(sys.argv[2]), sys.argv[3], sys.argv[4])

1
pytools/requirements.txt Normal file
View File

@ -0,0 +1 @@
pillow==10.2.0

View File

@ -1,7 +1,22 @@
load("@bazel_skylib//rules:run_binary.bzl", "run_binary")
cc_library(
name = "sys",
srcs = glob(["*.c"]),
hdrs = glob(["*.h"]),
srcs = glob(["*.c"]) + [":fonts/sys_font_small.c"],
hdrs = glob(["*.h"]) + [":fonts/sys_font_small.h"],
visibility = ["//visibility:public"],
deps = ["//device:device"]
deps = ["//device:device"],
)
run_binary(
name = "sys_font_small",
args = [
"sys_font_small",
"256",
"$(location :fonts/sys_font_small.png)",
"$(location :fonts/sys_font_small.c)"
],
srcs = [":fonts/sys_font_small.png"],
outs = [":fonts/sys_font_small.c"],
tool = "//pytools:font",
)

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@ -0,0 +1,8 @@
#ifndef CROCPARTY_SYS_FONT_SMALL_H
#define CROCPARTY_SYS_FONT_SMALL_H
#include "sys/sys.h"
extern sys_glyph sys_font_small[256];
#endif // CROCPARTY_SYS_FONT_SMALL_H

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -3,6 +3,7 @@
#include "sys_data.h"
#include "sys_graphics.h"
#include "fonts/sys_font_small.h"
/**
* Initialize sys.

View File

@ -6,6 +6,7 @@
typedef int32_t sys_i32;
typedef uint8_t sys_color;
typedef uint32_t sys_screen_color;
typedef uint64_t sys_glyph;
sys_screen_color sys_make_screen_color(uint8_t r, uint8_t g, uint8_t b);

View File

@ -11,6 +11,7 @@ void sys_pixel_internal_set(sys_i32 x, sys_i32 y, sys_color c);
void sys_scanline_internal_set(
sys_i32 x0, sys_i32 x1, sys_i32 y, sys_color c, bool fill
);
void sys_glyph_draw(sys_i32 x, sys_i32 y, sys_glyph g, sys_color c);
// == public ==
void sys_clip_set(sys_i32 x0, sys_i32 y0, sys_i32 x1, sys_i32 y1) {
@ -55,6 +56,18 @@ sys_color sys_pixel_get(sys_i32 x, sys_i32 y) {
return device_pixels[y][x];
}
void sys_print(char* str, sys_i32 x, sys_i32 y, sys_color col) {
sys_i32 x_orig = x;
for (sys_i32 i = 0;; i++) {
uint8_t c = str[i];
if (c == 0) { break; }
if (c == '\n') { x = x_orig; y += 8; continue; }
if (c == '\r') { x = x_orig; continue; }
sys_glyph_draw(x, y, sys_font_small[c], col);
x += 8;
}
}
void sys_cls(sys_color c) {
assert(sys_get_initialized());
@ -251,3 +264,16 @@ void sys_scanline_internal_set(
sys_pixel_internal_set(x1, y, c);
}
}
void sys_glyph_draw(sys_i32 x, sys_i32 y, sys_glyph g, sys_color c) {
// iterate through the bits of the glyph, and draw the character
// if that bit is set
for (int py = 0; py < 8; py++) {
for (int px = 0; px < 8; px++) {
uint64_t mask = ((uint64_t) 1) << (uint64_t) (py * 8 + px);
if ((g & mask) != 0) {
sys_pixel_internal_set(x + px, y + py, c);
}
}
}
}

View File

@ -54,7 +54,10 @@ sys_color sys_pixel_get(
// TODO: SSET/SGET
// TODO: FGET/FSET
// TODO: PRINT
/**
* Print a string `str` in the color `col`
*/
void sys_print(char* str, sys_i32 x, sys_i32 y, sys_color col);
// TODO: CURSOR? COLOR?
/**
@ -83,7 +86,7 @@ void sys_camera_reset();
* This is a special case of sys_circ_oval_draw_ext.
*/
void sys_circ_draw(sys_i32 x, sys_i32 y, sys_i32 r, sys_color c);
void sys_circ_fill(sys_i32 x, sys_i32 y, sys_i32 r);
void sys_circ_fill(sys_i32 x, sys_i32 y, sys_i32 r, sys_color c);
void sys_circ_draw_ext(sys_i32 x, sys_i32 y, sys_i32 r, sys_color c, bool fill);
/**