68 lines
2.2 KiB
Python
68 lines
2.2 KiB
Python
# Convert PNG/JPG icons to custom RGB565 raw format with header
|
|
# Output file format (little-endian): [uint16 width][uint16 height][pixels: width*height*2 bytes]
|
|
# Usage: python tools/convert_icons.py --src assets/icons_src --dst data/icons --size 48 48
|
|
|
|
import argparse
|
|
import os
|
|
from PIL import Image
|
|
|
|
|
|
def rgb888_to_rgb565(r, g, b):
|
|
# Swap r/b to match display byte order used in utils::rgbTo565
|
|
r, b = b, r
|
|
r5 = (r * 31) // 255
|
|
g6 = (g * 63) // 255
|
|
b5 = (b * 31) // 255
|
|
return (r5 << 11) | (g6 << 5) | b5
|
|
|
|
|
|
def convert_one(src_path, dst_path, out_w, out_h):
|
|
img = Image.open(src_path).convert('RGBA')
|
|
img = img.resize((out_w, out_h), Image.LANCZOS)
|
|
|
|
# Premultiply against black background for simple alpha handling
|
|
bg = Image.new('RGBA', (out_w, out_h), (0, 0, 0, 255))
|
|
img = Image.alpha_composite(bg, img)
|
|
|
|
# Write header + pixels
|
|
os.makedirs(os.path.dirname(dst_path), exist_ok=True)
|
|
with open(dst_path, 'wb') as f:
|
|
f.write(bytes([out_w & 0xFF, (out_w >> 8) & 0xFF, out_h & 0xFF, (out_h >> 8) & 0xFF]))
|
|
px = img.load()
|
|
for y in range(out_h):
|
|
for x in range(out_w):
|
|
r, g, b, a = px[x, y]
|
|
c = rgb888_to_rgb565(r, g, b)
|
|
f.write(bytes([c & 0xFF, (c >> 8) & 0xFF]))
|
|
print(f"Converted: {src_path} -> {dst_path}")
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('--src', default='assets/icons_src', help='Source icon folder')
|
|
parser.add_argument('--dst', default='data/icons', help='Destination folder in LittleFS data')
|
|
parser.add_argument('--size', nargs=2, type=int, default=[48, 48], help='Output icon size WxH')
|
|
args = parser.parse_args()
|
|
|
|
out_w, out_h = args.size
|
|
|
|
mapping = {
|
|
'timer': 'timer.r565',
|
|
'web': 'web.r565',
|
|
'game': 'game.r565',
|
|
}
|
|
|
|
for name, out_file in mapping.items():
|
|
src_file = os.path.join(args.src, f'{name}.png')
|
|
if not os.path.exists(src_file):
|
|
print(f"WARN: missing {src_file}, skip")
|
|
continue
|
|
dst_file = os.path.join(args.dst, out_file)
|
|
convert_one(src_file, dst_file, out_w, out_h)
|
|
|
|
print('Done. You can now upload FS: pio run -t uploadfs')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|