8bitlenser/lenser/ti99/convert/mono.py
2026-07-03 19:35:35 -07:00

44 lines
1.6 KiB
Python

"""TI-99/4A (TMS9918A) monochrome / tinted-mono mode.
Same 256x192, 2-colours-per-cell format as gm2, but matched by *luminance* to a
grey ramp (black -> grey -> white) so every cell is neutral -- no colour clash,
maximum perceived detail, a clean greyscale photo. Pick a base colour for a
tinted monochrome instead. Reuses the gm2 byte packing and viewer.
"""
from __future__ import annotations
import numpy as np
from ... import palette as c64pal
from ...convert import base
from .. import palette as tpal
from . import gm2
WIDTH, HEIGHT = gm2.WIDTH, gm2.HEIGHT
CELL_W, CELL_H = gm2.CELL_W, gm2.CELL_H
PIXEL_ASPECT = gm2.PIXEL_ASPECT
# Neutral ramp: black(1), grey(14), white(15). Lighter siblings for tinting.
NEUTRAL = [1, 14, 15]
SIBLINGS = {2: 3, 3: 2, 4: 5, 5: 4, 6: 9, 8: 9, 9: 8, 12: 14, 13: 9}
def convert(img_rgb, palette_name="tms9918", dither_mode="atkinson",
intensive=False, base_color=None):
plab = tpal.palette_lab()
prgb = tpal.get_palette().astype(np.uint8)
ramp = base.luminance_ramp(plab, NEUTRAL, base_color, SIBLINGS)
idx, sets, rows, cols, err = base.mono_render(
img_rgb, plab, ramp, WIDTH, HEIGHT, CELL_W, CELL_H, dither_mode, n_free=2)
pattern, colors = gm2._encode(idx, sets, rows, cols)
data = bytes(pattern) + bytes(colors)
return base.Conversion(
mode="mono", width=WIDTH, height=HEIGHT, pixel_aspect=PIXEL_ASPECT,
index_image=idx.astype(np.uint16), data=data, data_addr=0,
viewer="gm2", preview_rgb=prgb[idx], error=err,
meta={"palette": "tms9918", "dither": dither_mode, "base_color": base_color},
)