Compare commits

..

2 Commits

Author SHA1 Message Date
d05e382063 Move the image loader to shared 2024-02-26 16:05:07 -08:00
72599b7d00 Use Jinja2, start to support sprites 2024-02-26 16:01:47 -08:00
9 changed files with 129 additions and 35 deletions

View File

@ -19,6 +19,6 @@ pip = use_extension("@rules_python//python/extensions:pip.bzl", "pip")
pip.parse( pip.parse(
hub_name = "pytools_deps", hub_name = "pytools_deps",
python_version = "3.10", python_version = "3.10",
requirements_lock = "//pytools:requirements.txt", requirements_lock = "//pytools:requirements.lock",
) )
use_repo(pip, "pytools_deps") use_repo(pip, "pytools_deps")

View File

@ -1,6 +1,6 @@
{ {
"lockFileVersion": 3, "lockFileVersion": 3,
"moduleFileHash": "f69bdb13b0df2f6d309d415437228ebef31fa559567a65ecfe60de77e328ae1d", "moduleFileHash": "b7a605dff62d0b97615cf1feb669dc4d3ce630abf1e74374f624b71287684b40",
"flags": { "flags": {
"cmdRegistries": [ "cmdRegistries": [
"https://bcr.bazel.build/" "https://bcr.bazel.build/"
@ -72,7 +72,7 @@
"attributeValues": { "attributeValues": {
"hub_name": "pytools_deps", "hub_name": "pytools_deps",
"python_version": "3.10", "python_version": "3.10",
"requirements_lock": "//pytools:requirements.txt" "requirements_lock": "//pytools:requirements.lock"
}, },
"devDependency": false, "devDependency": false,
"location": { "location": {
@ -1741,10 +1741,60 @@
"os:windows,arch:amd64": { "os:windows,arch:amd64": {
"bzlTransitiveDigest": "EY+DGyyJhrqMHBCiplU6kCYiNtiUXl3Olt6+vF4JnfE=", "bzlTransitiveDigest": "EY+DGyyJhrqMHBCiplU6kCYiNtiUXl3Olt6+vF4JnfE=",
"accumulatedFileDigests": { "accumulatedFileDigests": {
"@@//pytools:requirements.txt": "ecb2c096486b1bb4dbe407a48fb3375ba496b6ea658c0cd7fc34571f381cd504" "@@//pytools:requirements.lock": "5a2eed9c6e50e9034521853f6016123e3e526900ddf3ff81cc1dbaaa28683629"
}, },
"envVariables": {}, "envVariables": {},
"generatedRepoSpecs": { "generatedRepoSpecs": {
"pytools_deps_310_jinja2": {
"bzlFile": "@@rules_python~0.31.0//python/pip_install:pip_repository.bzl",
"ruleClassName": "whl_library",
"attributes": {
"name": "rules_python~0.31.0~pip~pytools_deps_310_jinja2",
"requirement": "Jinja2==3.1.3",
"repo": "pytools_deps_310",
"repo_prefix": "pytools_deps_310_",
"whl_patches": {},
"experimental_target_platforms": [],
"python_interpreter": "",
"python_interpreter_target": "@@rules_python~0.31.0~python~python_3_10_host//:python",
"quiet": true,
"timeout": 600,
"isolated": true,
"extra_pip_args": [],
"download_only": false,
"pip_data_exclude": [],
"enable_implicit_namespace_pkgs": false,
"environment": {},
"envsubst": [],
"group_name": "",
"group_deps": []
}
},
"pytools_deps_310_markupsafe": {
"bzlFile": "@@rules_python~0.31.0//python/pip_install:pip_repository.bzl",
"ruleClassName": "whl_library",
"attributes": {
"name": "rules_python~0.31.0~pip~pytools_deps_310_markupsafe",
"requirement": "MarkupSafe==2.1.5",
"repo": "pytools_deps_310",
"repo_prefix": "pytools_deps_310_",
"whl_patches": {},
"experimental_target_platforms": [],
"python_interpreter": "",
"python_interpreter_target": "@@rules_python~0.31.0~python~python_3_10_host//:python",
"quiet": true,
"timeout": 600,
"isolated": true,
"extra_pip_args": [],
"download_only": false,
"pip_data_exclude": [],
"enable_implicit_namespace_pkgs": false,
"environment": {},
"envsubst": [],
"group_name": "",
"group_deps": []
}
},
"pytools_deps_310_pillow": { "pytools_deps_310_pillow": {
"bzlFile": "@@rules_python~0.31.0//python/pip_install:pip_repository.bzl", "bzlFile": "@@rules_python~0.31.0//python/pip_install:pip_repository.bzl",
"ruleClassName": "whl_library", "ruleClassName": "whl_library",
@ -1786,6 +1836,12 @@
"name": "rules_python~0.31.0~pip~pytools_deps", "name": "rules_python~0.31.0~pip~pytools_deps",
"repo_name": "pytools_deps", "repo_name": "pytools_deps",
"whl_map": { "whl_map": {
"jinja2": [
"3.10"
],
"markupsafe": [
"3.10"
],
"pillow": [ "pillow": [
"3.10" "3.10"
] ]

View File

@ -14,4 +14,12 @@ $ bazel run -c opt sdl_host.exe
``` ```
$ bazel run sdl_host.exe $ bazel run sdl_host.exe
```
## Dump requirements
(TODO: Automate)
```
$ pip freeze -r requirements.txt > requirements.lock
``` ```

View File

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

View File

@ -1,6 +1,13 @@
from typing import Tuple from typing import Tuple
from PIL import Image from PIL import Image
import sys import sys
import shared
TEMPLATE = """
// generated code! be nice!
#include "sys/sys.h"
sys_glyph {{ font_name }}[{{ n_glyphs }}] = { {{- glyphs|join(", ") -}} };
""".lstrip()
def main(font_name, n_glyphs, fname_png, fname_c): def main(font_name, n_glyphs, fname_png, fname_c):
@ -8,37 +15,32 @@ def main(font_name, n_glyphs, fname_png, fname_c):
assert(len(glyphs) == n_glyphs), f"must be exactly {n_glyphs} glyphs" assert(len(glyphs) == n_glyphs), f"must be exactly {n_glyphs} glyphs"
with open(fname_c, "wt") as output: with open(fname_c, "wt") as output:
output.writelines([ output.write(
"// generated code! be nice!\n", shared.templates.from_string(TEMPLATE).render(
"#include \"sys/sys.h\"\n", font_name=font_name,
f"sys_glyph {font_name}[{n_glyphs}] = {{{', '.join(str(g) for g in glyphs)}}};\n" n_glyphs=n_glyphs,
]) glyphs=glyphs,
)
)
def load_glyphs(fname_png: str): def load_glyphs(fname_png: str):
with Image.open(fname_png) as im: width, height, data = shared.load_image(fname_png)
width = im.width monochrome = [pixel_to_monochrome(p) for p in data]
height = im.height
assert width % 8 == 0, "width must be a multiple of 8" glyphs = []
assert height % 8 == 0, "height must be a multiple of 8" 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)
data = list(im.convert("RGBA").getdata()) return glyphs
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]): def pixel_to_monochrome(rgba: Tuple[int, int, int, int]):

View File

@ -0,0 +1,4 @@
pillow==10.2.0
Jinja2==3.1.3
## The following requirements were added by pip freeze:
MarkupSafe==2.1.5

View File

@ -1 +1,2 @@
pillow==10.2.0 pillow==10.2.0
Jinja2==3.1.3

17
pytools/shared.py Normal file
View File

@ -0,0 +1,17 @@
from PIL import Image
from jinja2 import Environment, BaseLoader, select_autoescape
templates = Environment(
loader=BaseLoader(),
autoescape=select_autoescape(),
)
def load_image(fname):
with Image.open(fname) 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"
return width, height, list(im.convert("RGBA").getdata())

View File

@ -3,10 +3,18 @@
#include <stdint.h> #include <stdint.h>
#define SYS_COLOR_TRANSPARENT 255
#define SYS_COLOR_N 256
#define SYS_SPRITE_H 8
#define SYS_SPRITE_W 8
typedef int32_t sys_i32; typedef int32_t sys_i32;
typedef uint8_t sys_color; typedef uint8_t sys_color;
typedef uint32_t sys_screen_color; typedef uint32_t sys_screen_color;
typedef uint64_t sys_glyph; typedef uint64_t sys_glyph;
typedef struct {
sys_color pixels[SYS_SPRITE_H][SYS_SPRITE_W];
} sys_sprite;
sys_screen_color sys_make_screen_color(uint8_t r, uint8_t g, uint8_t b); sys_screen_color sys_make_screen_color(uint8_t r, uint8_t g, uint8_t b);
@ -15,7 +23,4 @@ sys_i32 sys_min_i32(sys_i32 x, sys_i32 y);
sys_i32 sys_abs_i32(sys_i32 x); sys_i32 sys_abs_i32(sys_i32 x);
sys_i32 sys_sgn_i32(sys_i32 x); sys_i32 sys_sgn_i32(sys_i32 x);
#define SYS_COLOR_TRANSPARENT 255
#define SYS_COLOR_N 256
#endif // CROCPARTY_SYS_DATA_H #endif // CROCPARTY_SYS_DATA_H