Initial public release
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user