69 lines
2 KiB
Python
69 lines
2 KiB
Python
"""TRS-80 Color Computer MC6847 VDG palette + pixel packing.
|
|
|
|
PMODE 4 (256x192) is 2-colour: black + the foreground of the selected colour set
|
|
(CSS=0 green, CSS=1 "buff" ~ off-white). We use CSS=1 (buff on black) for clean
|
|
monochrome, like Apple HGR mono.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from ..palette import srgb_to_lab
|
|
|
|
# Approximate sRGB for the MC6847 colours.
|
|
BLACK = (0, 0, 0)
|
|
BUFF = (255, 255, 255)
|
|
GREEN = (38, 194, 64)
|
|
YELLOW = (255, 240, 112)
|
|
BLUE = (40, 62, 211)
|
|
RED = (180, 38, 40)
|
|
CYAN = (52, 198, 160)
|
|
MAGENTA = (200, 70, 180)
|
|
ORANGE = (224, 116, 36)
|
|
|
|
# PMODE 4 monochrome (CSS=1): index 0 = black, 1 = buff.
|
|
MONO = np.array([BLACK, BUFF], dtype=np.float64)
|
|
|
|
# PMODE 3 (CG6) 4-colour sets, selected by the CSS bit. The 2-bit pixel value
|
|
# 0..3 indexes the set. VDG byte ($FF22) high nibble = E (A/G,GM2,GM1=1, GM0=0).
|
|
PMODE3_SETS = {
|
|
0xE0: np.array([GREEN, YELLOW, BLUE, RED], dtype=np.float64), # CSS=0
|
|
0xE8: np.array([BUFF, CYAN, MAGENTA, ORANGE], dtype=np.float64), # CSS=1
|
|
}
|
|
|
|
|
|
def mono_lab() -> np.ndarray:
|
|
return srgb_to_lab(MONO)
|
|
|
|
|
|
def pack_pmode3(val: np.ndarray) -> bytes:
|
|
"""Pack a (192,128) 0..3 array into 6144 bytes, 4 pixels/byte (2bpp),
|
|
leftmost pixel in the high bits."""
|
|
h, w = val.shape
|
|
out = bytearray(w // 4 * h)
|
|
k = 0
|
|
for y in range(h):
|
|
row = val[y]
|
|
for x in range(0, w, 4):
|
|
out[k] = ((row[x] & 3) << 6) | ((row[x + 1] & 3) << 4) | \
|
|
((row[x + 2] & 3) << 2) | (row[x + 3] & 3)
|
|
k += 1
|
|
return bytes(out)
|
|
|
|
|
|
def pack_pmode4(idx: np.ndarray) -> bytes:
|
|
"""Pack a (192,256) 0/1 array into 6144 bytes, 8 pixels/byte, bit7 leftmost,
|
|
1 = foreground (the VDG reads this straight from video RAM)."""
|
|
h, w = idx.shape
|
|
out = bytearray(w // 8 * h)
|
|
k = 0
|
|
for y in range(h):
|
|
row = idx[y]
|
|
for x in range(0, w, 8):
|
|
b = 0
|
|
for i in range(8):
|
|
b = (b << 1) | (1 if row[x + i] else 0)
|
|
out[k] = b
|
|
k += 1
|
|
return bytes(out)
|