"""Atari 2600 TIA (NTSC) palette + playfield bit packing. The TIA colour byte is (hue << 4) | (lum << 1): 16 hues x 8 luminances = 128 colours (bit 0 unused, so register values are even 0..254). We generate the NTSC palette with a YIQ formula -- close enough to be recognisable; the encoder matches against it in CIELAB. """ from __future__ import annotations import numpy as np from ..palette import srgb_to_lab TIA_NTSC = [ (0,0,0), (44,44,44), (83,83,83), (119,119,119), (154,154,154), (188,188,188), (222,222,222), (255,255,255), (33,11,0), (73,53,0), (109,90,0), (145,126,0), (179,161,44), (213,195,83), (246,229,119), (255,255,154), (60,0,0), (97,35,0), (133,74,0), (168,110,26), (202,146,66), (235,180,103), (255,214,139), (255,247,173), (74,0,0), (111,18,0), (146,58,29), (181,96,68), (215,132,105), (248,167,141), (255,201,175), (255,234,209), (75,0,0), (112,5,43), (147,48,81), (182,86,118), (215,122,153), (249,157,187), (255,191,221), (255,225,254), (62,0,56), (99,1,93), (135,45,129), (170,83,164), (204,119,198), (237,154,232), (255,189,255), (255,222,255), (36,0,97), (75,7,133), (112,49,168), (147,87,202), (182,123,235), (215,159,255), (248,193,255), (255,226,255), (0,0,118), (43,21,153), (82,61,187), (118,99,221), (153,134,254), (188,169,255), (221,203,255), (254,236,255), (0,0,117), (10,38,152), (51,77,187), (89,113,220), (125,149,253), (160,183,255), (195,217,255), (228,250,255), (0,15,94), (0,56,130), (25,93,165), (65,129,199), (102,164,233), (138,198,255), (173,232,255), (206,255,255), (0,32,53), (0,71,91), (10,108,127), (52,143,162), (90,178,196), (126,212,229), (161,245,255), (195,255,255), (0,41,0), (0,80,38), (12,116,77), (53,152,114), (91,186,149), (127,220,183), (162,253,217), (196,255,250), (0,44,0), (0,82,0), (29,118,24), (69,154,64), (106,188,102), (141,221,137), (176,255,172), (210,255,206), (0,38,0), (14,77,0), (56,114,0), (93,149,24), (129,183,64), (164,217,101), (198,250,137), (232,255,171), (7,24,0), (50,64,0), (88,102,0), (124,137,0), (159,172,43), (193,206,82), (226,239,118), (255,255,153), (42,5,0), (80,48,0), (117,86,0), (152,122,6), (186,157,48), (220,191,86), (253,225,122), (255,255,158), ] PALETTE = np.array(TIA_NTSC, dtype=np.float64) # (128,3) sRGB, calibrated from MAME def color_byte(index: int) -> int: """TIA register value (even 0..254) for palette index hue*8+lum.""" hue, lum = divmod(index, 8) return (hue << 4) | (lum << 1) def palette_lab() -> np.ndarray: return srgb_to_lab(PALETTE) # ---- playfield packing ----------------------------------------------------- # The 20 playfield pixels (left to right) map to the PF registers in this order: # px 0-3 PF0 bits 4,5,6,7 # px 4-11 PF1 bits 7,6,5,4,3,2,1,0 # px 12-19 PF2 bits 0,1,2,3,4,5,6,7 def pack20(bits) -> tuple[int, int, int]: """20 pixel on/off values (leftmost first) -> (PF0, PF1, PF2) bytes.""" pf0 = pf1 = pf2 = 0 for i in range(4): if bits[i]: pf0 |= 1 << (4 + i) for i in range(8): if bits[4 + i]: pf1 |= 1 << (7 - i) for i in range(8): if bits[12 + i]: pf2 |= 1 << i return pf0, pf1, pf2