107 lines
3.3 KiB
Python
107 lines
3.3 KiB
Python
"""Apple II colour palettes and the HGR memory-layout helper.
|
|
|
|
- HGR mono: black/white (the 280x192 1-bit bitmap displayed as monochrome).
|
|
- HGR colour: 6 NTSC "artifact" colours (added later).
|
|
- DHGR: 16 colours (added later).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import numpy as np
|
|
|
|
from ..palette import srgb_to_lab
|
|
|
|
# Monochrome (white phosphor) pair.
|
|
MONO = np.array([(0, 0, 0), (255, 255, 255)], dtype=np.float64)
|
|
|
|
# Double-Hi-Res 16-colour palette, indexed by the 4-bit value -- measured from
|
|
# MAME's apple2ee DHGR output so the encoder's nibble values map to exactly what
|
|
# the //e displays.
|
|
DHGR16 = np.array([
|
|
(0x00, 0x00, 0x00), # 0 black
|
|
(0x40, 0x1c, 0xf7), # 1 blue
|
|
(0x00, 0x74, 0x40), # 2 dark green
|
|
(0x19, 0x90, 0xff), # 3 medium blue
|
|
(0x40, 0x63, 0x00), # 4 olive / dark green
|
|
(0x80, 0x80, 0x80), # 5 grey
|
|
(0x19, 0xd7, 0x00), # 6 green
|
|
(0x58, 0xf4, 0xbf), # 7 aqua
|
|
(0xa7, 0x0b, 0x40), # 8 dark red / magenta
|
|
(0xe6, 0x28, 0xff), # 9 magenta / violet
|
|
(0x80, 0x80, 0x80), # 10 grey
|
|
(0xbf, 0x9c, 0xff), # 11 lavender
|
|
(0xe6, 0x6f, 0x00), # 12 orange
|
|
(0xff, 0x8b, 0xbf), # 13 pink
|
|
(0xbf, 0xe3, 0x08), # 14 yellow-green
|
|
(0xff, 0xff, 0xff), # 15 white
|
|
], dtype=np.float64)
|
|
|
|
|
|
# HGR NTSC "artifact" colours. Per 7-pixel byte a palette bit selects one of two
|
|
# colour pairs; the displayed colour of an "on" pixel also depends on its column
|
|
# parity (and two adjacent on-pixels read as white).
|
|
# palette 0: even column -> violet, odd column -> green
|
|
# palette 1: even column -> blue, odd column -> orange
|
|
HGR_BLACK, HGR_VIOLET, HGR_GREEN, HGR_WHITE, HGR_BLUE, HGR_ORANGE = 0, 1, 2, 3, 4, 5
|
|
HGR6 = np.array([
|
|
(0x00, 0x00, 0x00), # black
|
|
(0xd0, 0x3a, 0xff), # violet
|
|
(0x20, 0xc8, 0x00), # green
|
|
(0xff, 0xff, 0xff), # white
|
|
(0x20, 0x9a, 0xff), # blue
|
|
(0xff, 0x6a, 0x20), # orange
|
|
], dtype=np.float64)
|
|
|
|
|
|
def mono_lab() -> np.ndarray:
|
|
return srgb_to_lab(MONO)
|
|
|
|
|
|
def hgr6_lab() -> np.ndarray:
|
|
return srgb_to_lab(HGR6)
|
|
|
|
|
|
def pack_hgr_color(bits280: np.ndarray, pal_byte: np.ndarray) -> bytes:
|
|
"""280x192 mono bits + (192x40) per-byte palette bit -> 8192 HGR buffer."""
|
|
buf = bytearray(0x2000)
|
|
H = bits280.shape[0]
|
|
for y in range(H):
|
|
base = hgr_row_addr(y)
|
|
row = bits280[y]
|
|
for bx in range(40):
|
|
b = 0
|
|
for i in range(7):
|
|
if row[bx * 7 + i]:
|
|
b |= (1 << i)
|
|
if pal_byte[y, bx]:
|
|
b |= 0x80
|
|
buf[base + bx] = b
|
|
return bytes(buf)
|
|
|
|
|
|
def dhgr_lab() -> np.ndarray:
|
|
return srgb_to_lab(DHGR16)
|
|
|
|
|
|
def hgr_row_addr(y: int) -> int:
|
|
"""Offset (from $2000) of HGR row ``y`` (0..191) in the interleaved layout."""
|
|
return (y & 7) * 0x400 + ((y >> 3) & 7) * 0x80 + (y >> 6) * 0x28
|
|
|
|
|
|
def pack_hgr_mono(val_image: np.ndarray) -> bytes:
|
|
"""280x192 1-bit image -> 8192-byte HGR page 1 buffer.
|
|
|
|
7 pixels per byte, bit 0 = leftmost, bit 7 (palette bit) = 0 for mono.
|
|
"""
|
|
buf = bytearray(0x2000)
|
|
H, W = val_image.shape
|
|
for y in range(H):
|
|
base = hgr_row_addr(y)
|
|
row = val_image[y]
|
|
for bx in range(40):
|
|
b = 0
|
|
for i in range(7):
|
|
if row[bx * 7 + i]:
|
|
b |= (1 << i)
|
|
buf[base + bx] = b
|
|
return bytes(buf)
|