kugelmuehle/gif2u8g2.py

60 lines
2.3 KiB
Python
Raw Permalink Normal View History

2026-09-26 01:10:32 +02:00
import os
import sys
from PIL import Image, ImageSequence
def gif_to_xbm(gif_path, output_h_path, var_name, target_w=None, target_h=None):
im = Image.open(gif_path)
frames = []
for frame in ImageSequence.Iterator(im):
f = frame.convert('RGBA')
# Weißer Hintergrund für transparente GIFs
bg = Image.new('RGBA', f.size, (255, 255, 255))
f = Image.alpha_composite(bg, f).convert('1') # 1-Bit Schwarz/Weiß
if target_w and target_h:
f = f.resize((target_w, target_h), Image.Resampling.NEAREST)
frames.append(f)
w, h = frames[0].size
bytes_per_row = (w + 7) // 8
with open(output_h_path, 'w') as out:
out.write(f"#pragma once\n#include <Arduino.h>\n\n")
out.write(f"// Abmessungen: {w}x{h}, {len(frames)} Frames\n")
out.write(f"#define {var_name.upper()}_WIDTH {w}\n")
out.write(f"#define {var_name.upper()}_HEIGHT {h}\n")
out.write(f"#define {var_name.upper()}_FRAME_COUNT {len(frames)}\n\n")
# 1. Einzelne Frames als XBM-Arrays schreiben (LSB-first)
for idx, frame in enumerate(frames):
out.write(f"const unsigned char {var_name}_frame_{idx}[] PROGMEM = {{\n ")
pixels = frame.load()
byte_list = []
for y in range(h):
for byte_idx in range(bytes_per_row):
byte_val = 0
for bit in range(8):
x = byte_idx * 8 + bit
if x < w:
# 0 = Schwarz (Pixel an bei XBM), 1 = Weiß
if pixels[x, y] == 0:
byte_val |= (1 << bit)
byte_list.append(f"0x{byte_val:02X}")
out.write(", ".join(byte_list))
out.write("\n};\n\n")
# 2. Master-Array aller Frame-Pointer
out.write(f"const unsigned char* const {var_name}_frames[] PROGMEM = {{\n")
for idx in range(len(frames)):
out.write(f" {var_name}_frame_{idx},\n")
out.write("};\n")
print(f"Fertig! {len(frames)} Frames exportiert nach: {output_h_path}")
# Aufruf: Datei, Ausgabedatei, Arrayname, Breite, Höhe
if __name__ == "__main__":
gif_to_xbm("fire.gif", "fire_frames.h", "fire_flames", 21, 32)
gif_to_xbm("clock.gif", "clock_frames.h", "clock", 21, 21)