Files
forge-invar/src/forge_invar/display.py
T

160 lines
5.1 KiB
Python

import time
from PIL import Image, ImageDraw, ImageFont, ImageMath
from .constants import (
ACTIVE_FX_RECT_BOUNDS,
ACTIVE_SRC_RECT_BOUNDS,
BACKGROUND_IMG_PATH,
ESTIMATED_BOOT_TIME_SECONDS,
FONT_1_PATH,
FONT_2_PATH,
FX_GROUP,
FXA_FRAG,
FXB_FRAG,
LOADING_IMG_PATH,
MFX_FRAG,
SELECTED_RECT_BOUNDS,
SRC_GROUP,
SRCA_FRAG,
SRCB_FRAG,
)
from .enums import ForgeState
from .forge_data import ForgeData
FONT_1 = ImageFont.truetype(FONT_1_PATH, 5)
FONT_2 = ImageFont.truetype(FONT_2_PATH, 8)
LOADING_IMG = Image.open(LOADING_IMG_PATH)
BACKGROUND_IMG = Image.open(BACKGROUND_IMG_PATH)
BLANK_IMG = Image.new("1", (128, 64), "black")
def __xor(img1: Image.Image, img2: Image.Image) -> Image.Image:
return ImageMath.lambda_eval(
lambda args: args["convert"](args["a"] ^ args["b"], "1"), a=img1, b=img2
)
def __get_loading_display(data: ForgeData, t0: float) -> Image.Image:
im = LOADING_IMG.copy()
draw = ImageDraw.Draw(im)
now = time.time()
percent = min(1.0, (now - t0) / ESTIMATED_BOOT_TIME_SECONDS)
draw.rectangle((24, 35, 24 + 80 * percent, 40), fill="white")
if data.state == ForgeState.INITIAL:
draw.circle((4, 4), 1, fill="white")
return im
def __get_running_display(data: ForgeData) -> Image.Image:
im = BACKGROUND_IMG.copy()
draw = ImageDraw.Draw(im)
im_overlay = BLANK_IMG.copy()
draw2 = ImageDraw.Draw(im_overlay)
# tempo
draw.text((42, -2), f"{int(data.tempo)}", fill="white", font=FONT_1)
draw.rectangle((6, 7, 6 + int(53 * data.beat_progress), 10), fill="white", width=0)
# time
draw.text((94, -2), f"{int(data.time)}", fill="white", font=FONT_1)
draw.rectangle(
(67, 7, 67 + int(53 * data.time_progress), 10), fill="white", width=0
)
# page
draw.circle((55 + 8 * data.page, 61), 2, fill="white")
# selected
draw.rectangle(SELECTED_RECT_BOUNDS[data.frag_selected], outline="white", fill=None)
# active
draw.rectangle(ACTIVE_SRC_RECT_BOUNDS[data.group_active(SRC_GROUP)], fill="white")
draw.rectangle(ACTIVE_FX_RECT_BOUNDS[data.group_active(FX_GROUP)], fill="white")
# fx/mix values
draw.rectangle((41, 16 + round(14 * (1 - data.fxa_value)), 55, 30), fill="white")
draw.rectangle((41, 40 + round(14 * (1 - data.fxb_value)), 55, 54), fill="white")
draw.rectangle((99, 28 + round(14 * (1 - data.mfx_value)), 113, 42), fill="white")
draw.rectangle((70, 28 + round(14 * (1 - data.mix_value)), 84, 42), fill="white")
# active shaders
draw2.text(
(16, 20), hex(data.frag_state(SRCA_FRAG))[2].upper(), fill="white", font=FONT_2
)
draw2.text(
(16, 44), hex(data.frag_state(SRCB_FRAG))[2].upper(), fill="white", font=FONT_2
)
draw2.text(
(45, 20), hex(data.frag_state(FXA_FRAG))[2].upper(), fill="white", font=FONT_2
)
draw2.text(
(45, 44), hex(data.frag_state(FXB_FRAG))[2].upper(), fill="white", font=FONT_2
)
draw2.text(
(103, 32), hex(data.frag_state(MFX_FRAG))[2].upper(), fill="white", font=FONT_2
)
# mix
draw2.text((74, 32), "K" if data.mix_mode_key else "M", fill="white", font=FONT_2)
# random states indicator
if data.frag_state_rand(SRCA_FRAG):
draw2.rectangle((23, 18, 24, 19), fill="white")
if data.frag_state_rand(SRCB_FRAG):
draw2.rectangle((23, 42, 24, 43), fill="white")
if data.frag_state_rand(FXA_FRAG):
draw2.rectangle((52, 18, 53, 19), fill="white")
if data.frag_state_rand(FXB_FRAG):
draw2.rectangle((52, 42, 53, 43), fill="white")
if data.frag_state_rand(MFX_FRAG):
draw2.rectangle((110, 30, 111, 31), fill="white")
# inverted fx
if data.fxa_inverted:
draw2.rectangle((43, 18, 53, 18), fill="white")
if data.fxb_inverted:
draw2.rectangle((43, 42, 53, 42), fill="white")
if data.mfx_inverted:
draw2.rectangle((101, 30, 111, 30), fill="white")
# feedback / output
draw_base = False
if data.frag_state(SRCA_FRAG) == 1:
draw_base = True
draw.rectangle((119, 22, 121, 36), fill="white")
draw.rectangle((119, 22, 127, 24), fill="white")
draw.rectangle((123, 21, 126, 25), fill=None, outline="black")
draw.rectangle((0, 22, 11, 24), fill="white")
draw.rectangle((1, 21, 4, 25), fill=None, outline="black")
if data.frag_state(SRCB_FRAG) == 1:
draw_base = True
draw.rectangle((119, 34, 121, 48), fill="white")
draw.rectangle((119, 46, 127, 48), fill="white")
draw.rectangle((123, 45, 126, 49), fill=None, outline="black")
draw.rectangle((0, 46, 11, 48), fill="white")
draw.rectangle((1, 45, 4, 49), fill=None, outline="black")
if not data.output_blank:
draw_base = True
draw.rectangle((119, 34, 128, 36), fill="white")
if draw_base:
draw.rectangle((114, 34, 121, 36), fill="white")
return __xor(im, im_overlay)
def get_display(data: ForgeData, t0: float) -> Image.Image:
if data.state == ForgeState.RUNNING:
return __get_running_display(data)
return __get_loading_display(data, t0)