Files
2026-07-17 15:29:53 -04:00

90 lines
3.0 KiB
Python

"""Image conversion utilities for ATOMS3 MacroPad."""
import math
import os
import random
import shutil
from PIL import Image, ImageTk
from .constants import SCREEN_W, SCREEN_H, IMAGES_DIR
def ensure_images_dir():
os.makedirs(IMAGES_DIR, exist_ok=True)
def generate_gradient_image() -> str:
"""Generate a random linear gradient PNG, save it to images dir, return its path."""
ensure_images_dir()
c1 = (random.randint(30, 220), random.randint(30, 220), random.randint(30, 220))
c2 = (random.randint(30, 220), random.randint(30, 220), random.randint(30, 220))
angle = math.radians(random.randint(0, 359))
cos_a = math.cos(angle)
sin_a = math.sin(angle)
size = SCREEN_W # 128x128 to match device display
img = Image.new("RGB", (size, size))
pixels = img.load()
for y in range(size):
for x in range(size):
nx = (x / (size - 1)) * 2 - 1
ny = (y / (size - 1)) * 2 - 1
t = max(0.0, min(1.0, (nx * cos_a + ny * sin_a + 1) / 2))
pixels[x, y] = (
int(c1[0] + (c2[0] - c1[0]) * t),
int(c1[1] + (c2[1] - c1[1]) * t),
int(c1[2] + (c2[2] - c1[2]) * t),
)
token = "%08x" % random.getrandbits(32)
path = os.path.join(IMAGES_DIR, f"gradient_{token}.png")
img.save(path)
return path
def copy_image_to_appdata(image_path: str) -> str:
"""Copy an image to the AppData images folder, return new path."""
ensure_images_dir()
basename = os.path.basename(image_path)
# Hash-suffix the filename to avoid collisions between same-named imports
name, ext = os.path.splitext(basename)
import hashlib
h = hashlib.md5(open(image_path, "rb").read()).hexdigest()[:8]
dest = os.path.join(IMAGES_DIR, f"{name}_{h}{ext}")
if not os.path.exists(dest):
shutil.copy2(image_path, dest)
return dest
def convert_to_rgb565(image_path: str) -> bytes:
"""Convert image to 128x128 RGB565 big-endian bytes (32768 bytes)."""
img = Image.open(image_path).convert("RGB")
img = img.resize((SCREEN_W, SCREEN_H), Image.LANCZOS)
data = bytearray(SCREEN_W * SCREEN_H * 2)
pixels = img.load()
for y in range(SCREEN_H):
for x in range(SCREEN_W):
r, g, b = pixels[x, y]
rgb565 = ((r & 0xF8) << 8) | ((g & 0xFC) << 3) | (b >> 3)
offset = (y * SCREEN_W + x) * 2
data[offset] = (rgb565 >> 8) & 0xFF # MSB
data[offset + 1] = rgb565 & 0xFF # LSB
return bytes(data)
def create_thumbnail(image_path: str, size: tuple = (64, 64)):
"""Create a tkinter-compatible thumbnail from an image file."""
img = Image.open(image_path).convert("RGB")
img.thumbnail(size, Image.LANCZOS)
return ImageTk.PhotoImage(img)
def create_preview(image_path: str, size: tuple = (128, 128)):
"""Larger preview image for the properties panel."""
img = Image.open(image_path).convert("RGB")
img = img.resize(size, Image.LANCZOS)
return ImageTk.PhotoImage(img)