First public commit.
This commit is contained in:
parent
2a48f52979
commit
4bac9d83ed
288 changed files with 18417 additions and 1076 deletions
70
lenser/bbc/palette.py
Normal file
70
lenser/bbc/palette.py
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
"""BBC Micro (Video ULA + 6845) palette and screen packing.
|
||||
|
||||
8 physical colours -- pure digital RGB. Modes map 1/2/4 bits-per-pixel of
|
||||
*logical* colour through a programmable palette (VDU 19) to these physicals.
|
||||
|
||||
Screen memory is character-cell interleaved: 8x8 cells ordered left-to-right then
|
||||
top-to-bottom; within a cell the bytes go by scanline, and each scanline spans
|
||||
1/2/4 bytes (2/4/8-colour). Pixel bits within a byte are interleaved, leftmost
|
||||
pixel in the high bits.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import numpy as np
|
||||
|
||||
from ..palette import srgb_to_lab
|
||||
|
||||
# Physical colours 0..7.
|
||||
PHYS = np.array([
|
||||
(0, 0, 0), # 0 black
|
||||
(255, 0, 0), # 1 red
|
||||
(0, 255, 0), # 2 green
|
||||
(255, 255, 0), # 3 yellow
|
||||
(0, 0, 255), # 4 blue
|
||||
(255, 0, 255), # 5 magenta
|
||||
(0, 255, 255), # 6 cyan
|
||||
(255, 255, 255), # 7 white
|
||||
], dtype=np.float64)
|
||||
|
||||
|
||||
def phys_lab() -> np.ndarray:
|
||||
return srgb_to_lab(PHYS)
|
||||
|
||||
|
||||
def mono_lab() -> np.ndarray:
|
||||
return srgb_to_lab(PHYS[[0, 7]]) # black + white
|
||||
|
||||
|
||||
def _byte_for_pixels(vals, bits_per_pixel):
|
||||
"""Encode the pixels covering one byte into the BBC interleaved layout.
|
||||
Leftmost pixel uses the highest bit of each bit-plane group."""
|
||||
n = len(vals) # pixels per byte (8/4/2)
|
||||
b = 0
|
||||
for bit in range(bits_per_pixel - 1, -1, -1): # high plane first
|
||||
for i, v in enumerate(vals): # left pixel first
|
||||
b = (b << 1) | ((v >> bit) & 1)
|
||||
return b
|
||||
|
||||
|
||||
def pack(idx: np.ndarray, width: int, bits_per_pixel: int) -> bytes:
|
||||
"""Pack a (height, width) logical-colour array into BBC screen bytes.
|
||||
|
||||
The BBC layout is universal: one byte holds ``ppb`` horizontally-adjacent
|
||||
pixels, and 8 consecutive bytes step down the 8 raster lines of that
|
||||
byte-column before moving one byte-column to the right; whole character rows
|
||||
(8 raster lines) then follow top-to-bottom. So
|
||||
addr = char_row*(num_byte_cols*8) + byte_col*8 + raster
|
||||
"""
|
||||
h, w = idx.shape
|
||||
ppb = 8 // bits_per_pixel # pixels per byte
|
||||
num_byte_cols = w // ppb
|
||||
row_stride = num_byte_cols * 8 # bytes per character row
|
||||
out = bytearray((w * h) // ppb)
|
||||
for y in range(h):
|
||||
base = (y // 8) * row_stride + (y % 8)
|
||||
for bc in range(num_byte_cols):
|
||||
x0 = bc * ppb
|
||||
vals = [int(idx[y, x0 + p]) for p in range(ppb)]
|
||||
out[base + bc * 8] = _byte_for_pixels(vals, bits_per_pixel)
|
||||
return bytes(out)
|
||||
Loading…
Add table
Add a link
Reference in a new issue