63 lines
2.1 KiB
Python
63 lines
2.1 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
|
|
from PIL import Image
|
|
|
|
VALID_ORIENTATIONS = {0, 90, 180, 270}
|
|
DEFAULT_ROW_BIT_ORDER = (0, 1, 2, 3, 4)
|
|
|
|
|
|
def apply_orientation(image: Image.Image, orientation: int) -> Image.Image:
|
|
if orientation not in VALID_ORIENTATIONS:
|
|
raise ValueError("orientation must be one of 0, 90, 180, 270")
|
|
|
|
if orientation == 0:
|
|
return image.copy()
|
|
if orientation == 90:
|
|
return image.transpose(Image.Transpose.ROTATE_270)
|
|
if orientation == 180:
|
|
return image.transpose(Image.Transpose.ROTATE_180)
|
|
return image.transpose(Image.Transpose.ROTATE_90)
|
|
|
|
|
|
def get_row_bit_order() -> tuple[int, int, int, int, int]:
|
|
raw = os.environ.get("MATRIX_ROW_BIT_ORDER", "").strip()
|
|
if not raw:
|
|
return DEFAULT_ROW_BIT_ORDER
|
|
|
|
try:
|
|
values = tuple(int(part.strip()) for part in raw.split(","))
|
|
except ValueError as exc:
|
|
raise ValueError("MATRIX_ROW_BIT_ORDER must be five comma-separated integers") from exc
|
|
|
|
if len(values) != 5 or sorted(values) != [0, 1, 2, 3, 4]:
|
|
raise ValueError("MATRIX_ROW_BIT_ORDER must be a permutation of 0,1,2,3,4")
|
|
return values
|
|
|
|
|
|
def apply_row_bit_order(image: Image.Image, bit_order: tuple[int, int, int, int, int]) -> Image.Image:
|
|
if bit_order == DEFAULT_ROW_BIT_ORDER:
|
|
return image.copy()
|
|
|
|
width, height = image.size
|
|
if height % 32 != 0:
|
|
raise ValueError("row bit remap requires a display height divisible by 32")
|
|
|
|
remapped = Image.new("RGB", image.size)
|
|
for y in range(height):
|
|
scan_row = y & 31
|
|
half_offset = y - scan_row
|
|
physical_scan_row = _permute_low_five_bits(scan_row, bit_order)
|
|
source_y = half_offset + physical_scan_row
|
|
remapped.paste(image.crop((0, source_y, width, source_y + 1)), (0, y))
|
|
return remapped
|
|
|
|
|
|
def _permute_low_five_bits(value: int, bit_order: tuple[int, int, int, int, int]) -> int:
|
|
result = 0
|
|
for driver_bit, physical_bit in enumerate(bit_order):
|
|
if value & (1 << driver_bit):
|
|
result |= 1 << physical_bit
|
|
return result
|