mirror of
https://github.com/waveplate/img2irc.git
synced 2025-06-03 08:54:06 +00:00
tools for generating glyph bitmaps for src/chars.rs
This commit is contained in:
parent
86d8167e94
commit
998d0d7a24
62
utils/README.md
Normal file
62
utils/README.md
Normal file
@ -0,0 +1,62 @@
|
||||
This tool converts an image containing Unicode glyphs into Rust-compatible bitmap arrays for use in the `img2irc` project.
|
||||
|
||||
## Workflow Overview
|
||||
|
||||
1. **Create input files:**
|
||||
|
||||
* An **image file** with glyphs rendered horizontally, without gaps. Essentially a cropped screenshot of the rendered characters with no spaces in between.
|
||||
* A **text file** listing the same glyphs separated by spaces.
|
||||
|
||||
2. **Generate JSON bitmap data (`mkbitmap.py`).**
|
||||
|
||||
3. **Convert JSON to Rust source (`mkrust.py`).**
|
||||
|
||||
4. Replace `src/chars.rs` in `img2irc` with the generated Rust file and recompile.
|
||||
|
||||
---
|
||||
|
||||
## `mkbitmap.py`
|
||||
|
||||
Slices an image into bitmaps and outputs JSON.
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Description |
|
||||
| ------------- | ------------------------------------------------ |
|
||||
| `chars_file` | Text file containing glyphs separated by spaces. |
|
||||
| `image_file` | Horizontal image of glyphs (no gaps). |
|
||||
| `--icw` | Input character width (pixels). |
|
||||
| `--ich` | Input character height (pixels). |
|
||||
| `--threshold` | Black/white pixel threshold (0–255). |
|
||||
| `--obw` | Output bitmap width. |
|
||||
| `--obh` | Output bitmap height. |
|
||||
| `--output` | Output JSON filename. |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
./mkbitmap.py chars.txt chars.png --icw 20 --ich 40 --threshold 128 --obw 10 --obh 20 --output chars.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## `mkrust.py`
|
||||
|
||||
Converts JSON bitmap data to Rust source code.
|
||||
|
||||
**Arguments:**
|
||||
|
||||
| Argument | Description |
|
||||
| ------------ | ----------------------------- |
|
||||
| `input_json` | JSON file from `mkbitmap.py`. |
|
||||
| `output_rs` | Output Rust source file. |
|
||||
|
||||
**Example:**
|
||||
|
||||
```bash
|
||||
./mkrust.py chars.json chars.rs
|
||||
```
|
||||
|
||||
Replace the resulting `chars.rs` in your `img2irc` project and recompile.
|
||||
|
||||
You can use the included `chars.txt` and `chars.png` as an example.
|
BIN
utils/chars.png
Normal file
BIN
utils/chars.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 14 KiB |
1
utils/chars.txt
Normal file
1
utils/chars.txt
Normal file
@ -0,0 +1 @@
|
||||
🬀 🬁 🬂 🬃 🬄 🬅 🬆 🬇 🬈 🬉 🬊 🬋 🬌 🬍 🬎 🬏 🬐 🬑 🬒 🬓 🬔 🬕 🬖 🬗 🬘 🬙 🬚 🬛 🬜 🬝 🬞 🬟 🬠 🬡 🬢 🬣 🬤 🬥 🬦 🬧 🬨 🬩 🬪 🬫 🬬 🬭 🬮 🬯 🬰 🬱 🬲 🬳 🬴 🬵 🬶 🬷 🬸 🬹 🬺 🬻 🬼 🬼 🬽 🬾 🬿 🭀 🭁 🭂 🭃 🭄 🭅 🭆 🭇 🭈 🭉 🭊 🭋 🭌 🭍 🭎 🭏 🭐 🭑 🭒 🭓 🭔 🭕 🭖 🭗 🭘 🭙 🭚 🭛 🭜 🭝 🭞 🭟 🭠 🭡 🭢 🭣 🭤 🭥 🭦 🭧 🭨 🭩 🭪 🭫 🭬 🭭 🭮 🭯 🭶 🭷 🭸 🭹 🭺 🭻 🭼 🭽 🭾 🭿 🮀 🮁 🮂 🮃 🮄 🮅 🮆 🮇 🮈 🮉 🮊 🮋 🮗 🮚 🮛 ▀ ▁ ▂ ▃ ▄ ▅ ▆ ▇ █ ▉ ▊ ▋ ▌ ▍ ▎ ▏ ▐ ▔ ▔ ▕ ▖ ▖ ▗ ▘ ▙ ▚ ▛ ▜ ▝ ▞ ▟ 🠴 🠵 🠶 🠷 🞀 🞁 🞂 🞃 🞍 𝅛 𝅇 𝅉 𝅋 𝅍 𝅏 𝅑 𝅓 ∎ ⎖ ⏴ ⏵ ⏶ ⏷ ⏹ ⏺ ⏽ │ ┃ ╸ ╷ ╸ ╹ ╻ ■ ▪ ▬ ▮ ▰ ▲ ▴ ▶ ▸ ▼ ▾ ◀ ◂ ◆ ● ◖ ◗ ◢ ◣ ◤ ◥ ❚
|
63
utils/mkbitmap.py
Normal file
63
utils/mkbitmap.py
Normal file
@ -0,0 +1,63 @@
|
||||
#!/usr/bin/env python3
|
||||
import argparse
|
||||
import json
|
||||
from PIL import Image
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser(
|
||||
description="Slice a row-image of glyphs into bitmaps and output JSON mapping."
|
||||
)
|
||||
p.add_argument('chars_file', help='Space-delimited glyphs text file (UTF-8)')
|
||||
p.add_argument('image_file', help='Image with glyphs in one row, no gaps')
|
||||
p.add_argument('--icw', type=int, required=True, help='Input char width (px)')
|
||||
p.add_argument('--ich', type=int, required=True, help='Input char height (px)')
|
||||
p.add_argument('--threshold', type=int, required=True, help='B/W threshold (0–255)')
|
||||
p.add_argument('--obw', type=int, required=True, help='Output bitmap width')
|
||||
p.add_argument('--obh', type=int, required=True, help='Output bitmap height')
|
||||
p.add_argument('--output', required=True, help='Output JSON filename')
|
||||
args = p.parse_args()
|
||||
|
||||
# 1) Read glyphs
|
||||
with open(args.chars_file, 'r', encoding='utf-8') as f:
|
||||
glyphs = f.read().strip().split(' ')
|
||||
|
||||
# 2) Load image & sanity-check
|
||||
im = Image.open(args.image_file).convert('L')
|
||||
w, h = im.size
|
||||
if h != args.ich:
|
||||
raise ValueError(f"Image height {h} ≠ ich {args.ich}")
|
||||
count = w // args.icw
|
||||
if count * args.icw != w:
|
||||
raise ValueError(f"Image width {w} not a multiple of icw {args.icw}")
|
||||
if len(glyphs) != count:
|
||||
raise ValueError(f"{len(glyphs)} glyphs but {count} blocks in image")
|
||||
|
||||
# 3) Build mapping glyph → grid
|
||||
mapping = {}
|
||||
for i, glyph in enumerate(glyphs):
|
||||
left = i * args.icw
|
||||
block = im.crop((left, 0, left + args.icw, args.ich))
|
||||
block = block.resize((args.obw, args.obh), Image.LANCZOS)
|
||||
data = list(block.getdata())
|
||||
bits = [1 if pix > args.threshold else 0 for pix in data]
|
||||
grid = [bits[row*args.obw:(row+1)*args.obw] for row in range(args.obh)]
|
||||
mapping[glyph] = grid
|
||||
|
||||
# 4) Write JSON manually so rows stay inline
|
||||
with open(args.output, 'w', encoding='utf-8') as out:
|
||||
out.write('{\n')
|
||||
total = len(mapping)
|
||||
for idx, (glyph, grid) in enumerate(mapping.items()):
|
||||
# properly quote the glyph
|
||||
key = json.dumps(glyph, ensure_ascii=False)
|
||||
out.write(f' {key}: [\n')
|
||||
for ridx, row in enumerate(grid):
|
||||
row_str = ','.join(str(bit) for bit in row)
|
||||
comma = ',' if ridx < len(grid) - 1 else ''
|
||||
out.write(f' [{row_str}]{comma}\n')
|
||||
out.write(' ]')
|
||||
out.write(',\n' if idx < total - 1 else '\n')
|
||||
out.write('}\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
65
utils/mkrust.py
Normal file
65
utils/mkrust.py
Normal file
@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import unicodedata
|
||||
import argparse
|
||||
import sys
|
||||
|
||||
def get_glyph_name(ch):
|
||||
"""
|
||||
Return the official Unicode name of ch, or its codepoint if unnamed.
|
||||
"""
|
||||
try:
|
||||
return unicodedata.name(ch)
|
||||
except ValueError:
|
||||
# Fallback to codepoint string
|
||||
return f"U+{ord(ch):04X}"
|
||||
|
||||
def format_rust_array(matrix):
|
||||
"""
|
||||
Given a 2D list of ints, return a Rust-style nested array string.
|
||||
"""
|
||||
lines = []
|
||||
for row in matrix:
|
||||
# join numbers with commas, no spaces after commas to match example
|
||||
line = "[" + ",".join(str(v) for v in row) + "],"
|
||||
lines.append(" " + line)
|
||||
return "\n".join(lines)
|
||||
|
||||
def main():
|
||||
parser = argparse.ArgumentParser(
|
||||
description="Convert JSON glyph bitmaps to a Rust source file."
|
||||
)
|
||||
parser.add_argument("input_json", help="Path to input JSON file")
|
||||
parser.add_argument("output_rs", help="Path to write Rust source file")
|
||||
args = parser.parse_args()
|
||||
|
||||
# Load JSON
|
||||
with open(args.input_json, "r", encoding="utf-8") as f:
|
||||
data = json.load(f)
|
||||
|
||||
if not data:
|
||||
print("No glyphs found in JSON.", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
# Determine dimensions from first glyph
|
||||
first_matrix = next(iter(data.values()))
|
||||
rows = len(first_matrix)
|
||||
cols = len(first_matrix[0]) if rows > 0 else 0
|
||||
|
||||
# Begin emitting Rust code
|
||||
with open(args.output_rs, "w", encoding="utf-8") as out:
|
||||
out.write("// This file was generated by mkrust.py\n")
|
||||
out.write(f"pub const GLYPH_BITMAPS: &[(char, [[u8; {cols}]; {rows}])] = &[\n")
|
||||
|
||||
for ch, matrix in data.items():
|
||||
name = get_glyph_name(ch)
|
||||
# comment with the Unicode name
|
||||
out.write(f" // {name}\n")
|
||||
out.write(f" ('{ch}', [\n")
|
||||
out.write(format_rust_array(matrix) + "\n")
|
||||
out.write(" ]),\n\n")
|
||||
|
||||
out.write("];\n")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
Loading…
Reference in New Issue
Block a user