8bitlenser/lenser/spectrum/palette.py
2026-07-03 19:35:35 -07:00

47 lines
1.3 KiB
Python

"""Sinclair ZX Spectrum colour palette.
The ULA has 8 base colours, each in a normal and a BRIGHT version (component
value 0xD7 normal, 0xFF bright; black is the same in both). Crucially the BRIGHT
bit is per 8x8 *cell*, shared by that cell's ink and paper -- so a cell's two
colours must both be normal or both be bright (they can't mix). We model this as
a 16-entry palette: indices 0-7 = normal, 8-15 = bright, where index & 7 is the
ink/paper colour number and index >> 3 is the BRIGHT bit.
"""
from __future__ import annotations
import numpy as np
from ..palette import srgb_to_lab
_N = 0xD7 # normal-brightness component
_B = 0xFF # bright component
def _ramp(v):
# colour order matches the Spectrum's INK/PAPER numbering 0..7
return [
(0, 0, 0), # 0 black
(0, 0, v), # 1 blue
(v, 0, 0), # 2 red
(v, 0, v), # 3 magenta
(0, v, 0), # 4 green
(0, v, v), # 5 cyan
(v, v, 0), # 6 yellow
(v, v, v), # 7 white
]
SPECTRUM = np.array(_ramp(_N) + _ramp(_B), dtype=np.float64)
# Cell colour pairs must come from one brightness group (shared BRIGHT bit).
NORMAL_GROUP = list(range(0, 8))
BRIGHT_GROUP = list(range(8, 16))
def get_palette() -> np.ndarray:
return SPECTRUM
def palette_lab() -> np.ndarray:
return srgb_to_lab(SPECTRUM)